Experiment

This experiment was designed as a first stage project to evaluate Parse snRNA preps within the auditory cortex.

All execution is performed in containerized environment and recorded in markdown for reproducibility. The container can be launched with docker run --rm 8787:8787 -e PASSWORD=$PASS -v $PWD:/home/rstudio alemenze/abrfseurat, and accessed with localhost:8787

Sample ingress

To load the samples into R, we first start with the raw matrix files from the parse pipeline. I strongly recommend starting from the raw matrices. Raw matrices will allow you to more agnostically filter when doing cross platform studies, perform soup reductions, and can reduce cell callers bias against granulocyte cell calling (not relevant here but still). We start with the full collection of droplets/encapsulated nuclei, and this will be performed sample-by-sample for instructional ease. and any minor tweaks we need to make for single samples.

###############################################

control.raw <- ReadParseBio('./input_data/Control//DGE_unfiltered/')
control <- CreateSeuratObject(control.raw, min.features=2)
counts <- GetAssayData(object = control, slot = "counts")
nonzero <- counts > 0
keep_genes <- Matrix::rowSums(nonzero) >= 10
filtered_counts <- counts[keep_genes, ]
control <- CreateSeuratObject(filtered_counts, meta.data=control@meta.data, project='control', assay='RNA')

control@meta.data$samp <- 'Control'

control.raw <- control
control[['percent.mito']] <- PercentageFeatureSet(control, pattern='^Mt-')
control[['percent.ribo']] <- PercentageFeatureSet(control,pattern='^Rp[sl][[:digit:]]')
control[['percent.hb']] <- PercentageFeatureSet(control, pattern = "^hbb|hba")

#Make a quick plot of the input QC
prefilt_plot <- VlnPlot(control, features=c('nFeature_RNA','nCount_RNA','percent.mito','percent.ribo','percent.hb'), ncol=1, group.by='samp', raster=F)

prefilt_plot

Filtering

A critical part of data ingress is filtering the data, and required for all types of samples. The old adage still stands strong- garbage in = garbage out. We can see just from above that there are 11952 droplets total with captured nucleotides. The often majority are junk droplets with random floating nucleotides in the solution (aka the soup)- which we can filter out.

In this case we start with a fairly light filter, removing those which are more likely true noise or degraded.

# Remove all droplets with >7000 features, strong likelihood of homotypic doublets
# Remove all droplets with <100 features, likely noise
# Remove all droplets with <1000 counts of RNA, low capture droplets
# Remove all droplets with >10% mitochondrial content, likely dead/dying cells
# Remove all droplets with >45% ribosomal and 20% hemoglobin- likely just mess droplets

control <- subset (control, subset= nFeature_RNA < 7000 & percent.mito < 10 & percent.ribo < 45 & nCount_RNA > 1000 & nFeature_RNA > 100 & percent.hb < 20)

#Make a quick plot of the input QC
postfilt_plot <- VlnPlot(control, features=c('nFeature_RNA','nCount_RNA','percent.mito','percent.ribo','percent.hb'), ncol=1, group.by='samp',raster=F)

postfilt_plot

At the end of the filtering stages, we now have a final of 11952 droplets that we can support as cells.

Soup reduction

As mentioned before, there are ambient nucleotides in the solution that get captured as “droplets”. We identified droplets we defined as “noise” earlier, so now we can attempt to correct this issue.

Normalization and regression

Now we get into normalizing the samples, regressing non-viable data, and preparing the samples for integration (if applicable). Earlier workflows of scRNA data used a simple log-transformation as a normalization procedure. This has been shown to be insufficient as it primarily only corrects for depth. Current best practices include using regularized negative binomial regression- within Seurat being applied by SCTransform. This step also can be used to regress out things like mitochondrial content and ribosomal content as major drivers of differentiation- though as we saw above it can still be a major facet of clusters.

control <- SCTransform(control, vars.to.regress=c('percent.mito','percent.ribo'), verbose=FALSE, method='glmGamPoi')
control <- RunPCA(control)
control <- RunUMAP(control, dims=1:30)
control <- FindNeighbors(control, reduction='pca', dims=1:30)
resolutions <- c(0.2, 0.3, 0.4, 0.5, 0.6, 0.7,0.8, 0.9,1)
control <- FindClusters(control, resolution=c(resolutions))
plot_grid(DimPlot(control, group.by='SCT_snn_res.0.2', label=T) + NoLegend(),
          DimPlot(control, group.by='SCT_snn_res.0.3', label=T) + NoLegend(),
          DimPlot(control, group.by='SCT_snn_res.0.4', label=T) + NoLegend(),
          DimPlot(control, group.by='SCT_snn_res.0.5', label=T) + NoLegend(),
          DimPlot(control, group.by='SCT_snn_res.0.6', label=T) + NoLegend(),
          DimPlot(control, group.by='SCT_snn_res.0.7', label=T) + NoLegend(),
          DimPlot(control, group.by='SCT_snn_res.0.8', label=T) + NoLegend(),
          DimPlot(control, group.by='SCT_snn_res.0.9', label=T) + NoLegend(),
          DimPlot(control, group.by='SCT_snn_res.1', label=T) + NoLegend(),
          ncol=3
          )

Now by looking at these we can see a fair bit of segregation within the clusters. Overall, I would choose the 0.2 resolution as the one most likely to give nice biological separation, and we can plot it out.

#clean up the robject a bit
#rm(list= ls()[!(ls() %in% c('combined','souplist' ))])
gc()
#Define the default resolution and plot it out
DefaultAssay(control) <- 'SCT'
Idents(control) <- 'SCT_snn_res.0.2'
control[['seurat_clusters']] <- Idents(control)

DimPlot(control, group.by='seurat_clusters', label=T, pt.size=1.5, ncol=2) + NoLegend()

Annotation

Now lets try to annotate these clusters. This can be done based on alignment to public databases (HCA, celldex, etc), label transfers from previous projects, or manual curation. Usually this requires a high level of domain-specific expertise and requires biological collaboration. Since these are immune cells, we will use a quick automated test and some canonical markers.

Manual curation

Cluster markers

The first step is usually identifying the top markers for each cluster. This will give you the expression compared to all other cells, as well as the percentage of cells in the respective cluster/all other cells that express the gene. From there you can start to try and assign annotations to each.

Idents(control) <- 'seurat_clusters'
control <- PrepSCTFindMarkers(control)
markers <- FindAllMarkers(control, only.pos=T, min.diff.pct = 0.4)
datatable(markers)
Plotting canonical markers

Likewise, we can simply plot out markers we envision as canonical markers to interrogate the data further for annotations.

genes <- c('Aqp4','Gfap','Fgfr3','Slc14a1','Id3','Gja1','Gpr37l1','Camk2a','Cabra5','Islr2','Filip1','Rgs5','Ostn','Unc5b','Vamp1','Cacna1g','Smarca2','Fam198b','Smyd2','Cd63','Cntnap5','Phldb2','Arc','Tnk2','Sncb','Bcl2l1','Inpp5j','Igfbp2','Socs2','Htr2c','Scgn','Usp36','Abca8','Vip','Rps15a','Cdh3','Prkcg','Nos1','Penk','Gpm6a','Mapk6','Fuom','Pkia','Cplx2','Trdn','Slc17a6','Grm2','Pnoc','Slc6a3','Th','Slc18a2','Drd2','Slc32a1','Gad1','Gad2','Htr2c','Oprm1','Mog','Mog1','Pdgfra','C1qb','Msmo1','Syt10','Klhl4','Pcdh17','Ramp1','Ttn','Dbi','Reln','Lhx6','Trpc3','Chrd','Slc24a2','Sec11c','Mkx','Htr2c','Nxph4','Syt6','Cck','Npy','Krt17','Cbln4','Gpr83','Wfdc2')

p <- VlnPlot(control, features=c(genes), stack=TRUE, sort=TRUE, flip=TRUE,group.by='seurat_clusters') + theme(legend.position='none')
p

Assigning Annotations

Based on the combination of manual curation and dissection of marker genes (see paper for more details and the biology of it, as well as citations to various markers used), we assigned the specific annotations to the clusters as below.


Idents(control) <- 'seurat_clusters'
control <- StashIdent(control, save.name='seurat_original_clusters')

control <- RenameIdents(control, "0"="Excitatory Neurons 1")
control <- RenameIdents(control, "1"="Excitatory Neurons 2")
control <- RenameIdents(control, "2"="Excitatory Neurons 3")
control <- RenameIdents(control, "3"="Excitatory Neurons 4")
control <- RenameIdents(control, "4"="Excitatory Neurons 5")
control <- RenameIdents(control, "5"="Oligodendrocytes")
control <- RenameIdents(control, "6"="PV Inhibitory Neurons")
control <- RenameIdents(control, "7"="SST Inhibitory Neurons")
control <- RenameIdents(control, "8"="Unclassified Neurons 1")
control <- RenameIdents(control, "9"="VIP Inhibitory Neurons")
control <- RenameIdents(control, "10"="Astrocytes")
control <- RenameIdents(control, "11"="Unclassified Neurons 2")
control <- RenameIdents(control, "12"="OPCs")
control <- RenameIdents(control, "13"="Excitatory Neurons 7")
control <- RenameIdents(control, "14"="Excitatory Neurons 6")
control <- RenameIdents(control, "15"="Microglia")

control[['Annotations']] <- Idents(object=control)

DimPlot(control, group.by='Annotations', label=T, pt.size=1.75, label.size=5, cols=c("Excitatory Neurons 1"="#f8b6ff", "Excitatory Neurons 2"="#f090fa", "Excitatory Neurons 3"="#fda4ff","Excitatory Neurons 4"="#d0a4ff","Excitatory Neurons 5"="#ff69b4","PV Inhibitory Neurons"="#72b2f4","SST Inhibitory Neurons"="#348feb","Unclassified Neurons 1"="#ffe599","Unclassified Neurons 2"="#ffd966","VIP Inhibitory Neurons"="#82b6e5","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a"), repel=T)

Plotting more canonical markers
genes <- c('Chrm3','Cux2','Unc5d','Dgkb','Rorb','Thsd7a','Camk4','Sema3e','Adamts3','Il1rapl2','Plcxd3','Thsd7a','Garnl3','Foxp2','Zfpm2','Gad1','Gad2','Slc6a1','Eya4','Reln','Grin3a','Cdh9','Sema3c','Grm8','Slit2','Tox','Vip','Gabrg3','Cnr1','Lypd6','Rgs12','B3gat2','Mgat4c','Mbp','Mobp','Mag','Mal','Mog','Tubb4a','Ptprz1','Pdgfra','Sox6','Arhgap31','Gpr17','Vcan','Slc1a2','Slc1a3','Fam107a','S100b','Ndrg2','Apoe','Cx3cr1','Ctss','Slco2b1','Zfhx3','Inpp5d','Fibcd1','Gpr161','Fibcd1','Cpne7','Scn3b','Nxph1','Olfm3','Neto2','Trpc3')
p <- VlnPlot(control, features=c(genes), stack=TRUE, sort=FALSE, flip=TRUE,group.by='Annotations',cols=c("Excitatory Neurons 1"="#f8b6ff", "Excitatory Neurons 2"="#f090fa", "Excitatory Neurons 3"="#fda4ff","Excitatory Neurons 4"="#d0a4ff","Excitatory Neurons 5"="#ff69b4","PV Inhibitory Neurons"="#72b2f4","SST Inhibitory Neurons"="#348feb","Unclassified Neurons 1"="#ffe599","Unclassified Neurons 2"="#ffd966","VIP Inhibitory Neurons"="#82b6e5","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a"), fill.by='ident') + theme(legend.position='none')
p

Reclustering Astrocytes

A point of particular interest in this study was astrocytes, so to dive further into those we reclustered just the astrocyte cluster to attempt to elucidate the diversity within.

Idents(control) <- 'seurat_clusters'
astros <- subset(control, idents=c(10))
DefaultAssay(astros) <- 'RNA'

astros <- DietSeurat(astros, assays='RNA')
astros <- SCTransform(astros, vars.to.regress = c('percent.mito','percent.ribo'),verbose=FALSE)
astros <- RunPCA(astros)
astros <- RunUMAP(astros, dims=1:30)
astros <- FindNeighbors(astros, reduction='pca', dims=1:30)
resolutions <- c(0.2, 0.3, 0.4, 0.5, 0.6, 0.7,0.8, 0.9,1)
astros <- FindClusters(astros, resolution=c(resolutions))
plot_grid(DimPlot(astros, group.by='SCT_snn_res.0.2', label=T) + NoLegend(),
          DimPlot(astros, group.by='SCT_snn_res.0.3', label=T) + NoLegend(),
          DimPlot(astros, group.by='SCT_snn_res.0.4', label=T) + NoLegend(),
          DimPlot(astros, group.by='SCT_snn_res.0.5', label=T) + NoLegend(),
          DimPlot(astros, group.by='SCT_snn_res.0.6', label=T) + NoLegend(),
          DimPlot(astros, group.by='SCT_snn_res.0.7', label=T) + NoLegend(),
          DimPlot(astros, group.by='SCT_snn_res.0.8', label=T) + NoLegend(),
          DimPlot(astros, group.by='SCT_snn_res.0.9', label=T) + NoLegend(),
          DimPlot(astros, group.by='SCT_snn_res.1', label=T) + NoLegend(),
          ncol=3
          )

DefaultAssay(astros) <- 'SCT'
Idents(astros) <- 'SCT_snn_res.0.5'
astros[['seurat_clusters']] <- Idents(astros)

DimPlot(astros, group.by='seurat_clusters', label=T, pt.size=1.5, ncol=2) + NoLegend()

Idents(astros) <- 'seurat_clusters'
astros <- PrepSCTFindMarkers(astros)
astromarkers <- FindAllMarkers(astros, only.pos=T, min.diff.pct = 0.4)
datatable(astromarkers)

Astrocyte Pathways

Once we had identified the marker genes per astrocyte cluster, we wanted to view how the different populations were different from each other to try and delineate function. For this we used over-representation analysis against KEGG and GO databases.

library(clusterProfiler)
library(msigdbr)
library(stringr)
library(enrichplot)

BiocManager::install("org.Rn.eg.db")
n
organism='org.Rn.eg.db'
korg='rno'

#####################################
dir.create('./ORA_astros')
dir.create('./ORA_astros/KEGG')
dir.create('./ORA_astros/GO')
dir.create('./ORA_astros/GO/BP')
dir.create('./ORA_astros/GO/CC')
dir.create('./ORA_astros/GO/MF')


for (i in unique(Idents(astros))){
  marks <- FindMarkers(astros, ident.1=i, only.pos=T, min.diff.pct = 0.3, logfc.threshold = 0.5)
  marks <- subset(marks, p_val_adj < 0.05)
  genes <- rownames(marks)
  
  ids <- bitr(genes, fromType='SYMBOL', toType='ENTREZID', OrgDb=organism)
  
  run <- try({
    ora <- enrichGO(gene=ids$ENTREZID, ont='BP', keyType='ENTREZID',OrgDb=organism)
    
    plot <- dotplot(ora, showCategory=10, font.size=10) + scale_y_discrete(labels=function(x) str_wrap(x, width=40))
    jpeg(paste('./ORA_astros/GO/BP/cluster_',i,'_GO_BP_ORA.jpg',sep=''), height=800, width=800)
    print(plot)
    dev.off()
    
    write.csv(ora, file=paste('./ORA_astros/GO/BP/cluster_',i,'_GO_BP_ORA.csv',sep=''))
  })
  if(inherits(run, 'try-error')) {
    next
  }
  run <- try({
    ora <- enrichGO(gene=ids$ENTREZID, ont='CC', keyType='ENTREZID',OrgDb=organism)
    
    plot <- dotplot(ora, showCategory=10, font.size=10) + scale_y_discrete(labels=function(x) str_wrap(x, width=40))
    jpeg(paste('./ORA_astros/GO/CC/cluster_',i,'_GO_CC_ORA.jpg',sep=''), height=800, width=800)
    print(plot)
    dev.off()
    
    write.csv(ora, file=paste('./ORA_astros/GO/CC/cluster_',i,'_GO_CC_ORA.csv',sep=''))
  })
  if(inherits(run, 'try-error')) {
    next
  }
  
  run <- try({
    ora <- enrichGO(gene=ids$ENTREZID, ont='BP', keyType='ENTREZID',OrgDb=organism)
    
    plot <- dotplot(ora, showCategory=10, font.size=10) + scale_y_discrete(labels=function(x) str_wrap(x, width=40))
    jpeg(paste('./ORA_astros/GO/MF/cluster_',i,'_GO_MF_ORA.jpg',sep=''), height=800, width=800)
    print(plot)
    dev.off()
    
    write.csv(ora, file=paste('./ORA_astros/GO/MF/cluster_',i,'_GO_MF_ORA.csv',sep=''))
  })
  if(inherits(run, 'try-error')) {
    next
  }
  
  run <- try({
    ora <- enrichKEGG(gene=ids$ENTREZID, organism=korg)
    
    plot <- dotplot(ora, showCategory=10, font.size=10) + scale_y_discrete(labels=function(x) str_wrap(x, width=40))
    jpeg(paste('./ORA_astros/KEGG/cluster_',i,'_KEGG_ORA.jpg',sep=''), height=800, width=800)
    print(plot)
    dev.off()
    
    write.csv(ora, file=paste('./ORA_astros/KEGG/cluster_',i,'_KEGG_ORA.csv',sep=''))
  })
  if(inherits(run, 'try-error')) {
    next
  }
  
}

Reclustering Inhibitory neurons

Likewise we dug into the inhibitory neuron subsets.


Idents(control) <- 'Annotations'
inhib <- subset(control, idents=c('PV Inhibitory Neurons','VIP Inhibitory Neurons','SST Inhibitory Neurons'))
DefaultAssay(inhib) <- 'RNA'

inhib <- DietSeurat(inhib, assays='RNA')
inhib <- SCTransform(inhib, vars.to.regress = c('percent.mito','percent.ribo'),verbose=FALSE)
inhib <- RunPCA(inhib)
inhib <- RunUMAP(inhib, dims=1:30)
inhib <- FindNeighbors(inhib, reduction='pca', dims=1:30)
resolutions <- c(0.2, 0.3, 0.4, 0.5, 0.6, 0.7,0.8, 0.9,1)
inhib <- FindClusters(inhib, resolution=c(resolutions))
plot_grid(DimPlot(inhib, group.by='SCT_snn_res.0.2', label=T) + NoLegend(),
          DimPlot(inhib, group.by='SCT_snn_res.0.3', label=T) + NoLegend(),
          DimPlot(inhib, group.by='SCT_snn_res.0.4', label=T) + NoLegend(),
          DimPlot(inhib, group.by='SCT_snn_res.0.5', label=T) + NoLegend(),
          DimPlot(inhib, group.by='SCT_snn_res.0.6', label=T) + NoLegend(),
          DimPlot(inhib, group.by='SCT_snn_res.0.7', label=T) + NoLegend(),
          DimPlot(inhib, group.by='SCT_snn_res.0.8', label=T) + NoLegend(),
          DimPlot(inhib, group.by='SCT_snn_res.0.9', label=T) + NoLegend(),
          DimPlot(inhib, group.by='SCT_snn_res.1', label=T) + NoLegend(),
          ncol=3
          )

Idents(inhib) <- 'seurat_clusters'
inhib <- PrepSCTFindMarkers(inhib)
inhibmarkers <- FindAllMarkers(inhib, only.pos=T, min.diff.pct = 0.4)
datatable(inhibmarkers)

Inhibitory pathways analysis

Reclustering Excitatory neurons

And once again for excitatory neurons!

Idents(control) <- 'Annotations'
excit <- subset(control, idents=c('Excitatory Neurons 1','Excitatory Neurons 2','Excitatory Neurons 3','Excitatory Neurons 4','Excitatory Neurons 5'))
DefaultAssay(excit) <- 'RNA'

excit <- DietSeurat(excit, assays='RNA')
excit <- SCTransform(excit, vars.to.regress = c('percent.mito','percent.ribo'),verbose=FALSE)
excit <- RunPCA(excit)
excit <- RunUMAP(excit, dims=1:30)
excit <- FindNeighbors(excit, reduction='pca', dims=1:30)
resolutions <- c(0.2, 0.3, 0.4, 0.5, 0.6, 0.7,0.8, 0.9,1)
excit <- FindClusters(excit, resolution=c(resolutions))
plot_grid(DimPlot(excit, group.by='SCT_snn_res.0.2', label=T) + NoLegend(),
          DimPlot(excit, group.by='SCT_snn_res.0.3', label=T) + NoLegend(),
          DimPlot(excit, group.by='SCT_snn_res.0.4', label=T) + NoLegend(),
          DimPlot(excit, group.by='SCT_snn_res.0.5', label=T) + NoLegend(),
          DimPlot(excit, group.by='SCT_snn_res.0.6', label=T) + NoLegend(),
          DimPlot(excit, group.by='SCT_snn_res.0.7', label=T) + NoLegend(),
          DimPlot(excit, group.by='SCT_snn_res.0.8', label=T) + NoLegend(),
          DimPlot(excit, group.by='SCT_snn_res.0.9', label=T) + NoLegend(),
          DimPlot(excit, group.by='SCT_snn_res.1', label=T) + NoLegend(),
          ncol=3
          )
DefaultAssay(excit) <- 'SCT'
Idents(excit) <- 'SCT_snn_res.0.2'
excit[['seurat_clusters']] <- Idents(excit)

DimPlot(excit, pt.size=2, label=T,cols=c("Excitatory Neurons 1"="#f8b6ff", "Excitatory Neurons 2"="#f090fa", "Excitatory Neurons 3"="#fda4ff","Excitatory Neurons 4"="#d0a4ff","Excitatory Neurons 5"="#ff69b4","PV Inhibitory Neurons"="#72b2f4","SST Inhibitory Neurons"="#348feb","Unclassified Neurons 1"="#ffe599","Unclassified Neurons 2"="#ffd966","VIP Inhibitory Neurons"="#82b6e5","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a"), group.by='Annotations')
Idents(excit) <- 'seurat_clusters'
excit <- PrepSCTFindMarkers(excit)
excitmarkers <- FindAllMarkers(excit, only.pos=T, min.diff.pct = 0.4)
datatable(excitmarkers)

Excitatory pathway analysis

Publication plots
###########################################################
# Figure 1
## 1A: UMAP plot
## 1B: Composition plot

temp <- control
temp <- subset(temp, idents=c('Excitatory Neurons 1', 'Excitatory Neurons 2','Excitatory Neurons 3','Excitatory Neurons 4','Excitatory Neurons 5','Excitatory Neurons 6','SST Inhibitory Neurons','PV Inhibitory Neurons','VIP Inhibitory Neurons','Microglia','OPCs','Astrocytes','Oligodendrocytes','Unclassified Neurons 1','Unclassified Neurons 2'))

temp@meta.data$Annotations <-stringr::str_wrap(temp@meta.data$Annotations, 15)
p1a <- DimPlot(temp, group.by='Annotations', label=T, pt.size=1.75, label.size=4, cols=c("Excitatory\nNeurons 1"="#f8b6ff", "Excitatory\nNeurons 2"="#f090fa", "Excitatory\nNeurons 3"="#fda4ff","Excitatory\nNeurons 4"="#d0a4ff","Excitatory\nNeurons 5"="#ff69b4","PV Inhibitory\nNeurons"="#72b2f4","SST Inhibitory\nNeurons"="#348feb","Unclassified\nNeurons 1"="#ffe599","Unclassified\nNeurons 2"="#ffd966","VIP Inhibitory\nNeurons"="#1c51e6","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory\nNeurons 6"="#efd68b","Excitatory\nNeurons 7"="#b1c37c","Oligodendrocytes"="#59c52a"), repel=T) + NoLegend() + ggtitle(NULL)

counts <- table(Idents(temp), temp$samp)
counts <- prop.table(counts, margin=2)*100
#Organize the dataframe for easy plotting
df <- as.data.frame.matrix(counts) 
df$cluster <- rownames(df)
df <- df %>% gather(., treatment, props, Control:Control, factor_key=T) 
group.colors <- c("Excitatory Neurons 1"="#f8b6ff", "Excitatory Neurons 2"="#f090fa", "Excitatory Neurons 3"="#fda4ff","Excitatory Neurons 4"="#d0a4ff","Excitatory Neurons 5"="#ff69b4","PV Inhibitory Neurons"="#72b2f4","SST Inhibitory Neurons"="#348feb","Unclassified Neurons 1"="#ffe599","Unclassified Neurons 2"="#ffd966","VIP Inhibitory Neurons"="#1c51e6","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a")

p1b <- ggplot(df, aes(x=treatment, y=props, fill=cluster)) + geom_bar(stat='identity', color='black') + theme_minimal() + labs(title='',x='',y='Percentage of cells', fill='Cluster:') + theme(text=element_text(size=20), axis.text.x = element_text(angle = 315, size=0)) + scale_fill_manual(values=group.colors) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
panel.background = element_blank(), axis.line = element_line(colour = "black"))

title <- ggdraw() + 
  draw_label(
    "Figure 1",
    fontface = 'bold',
    x = 0,
    hjust = 0,
    size=20
  ) +
  theme(
    # add margin on the left of the drawing canvas,
    # so title is aligned with left edge of first plot
    plot.margin = margin(0, 0, 0, 7)
  )

plots <- cowplot::plot_grid(p1a, p1b, ncol=2, rel_widths=c(0.6,0.4), labels=c('A','B'))
f1 <- cowplot::plot_grid(title, plots, ncol=1, rel_heights=c(0.1,1))

###########################################################
# Figure 2
## 2A: UMAP plot for Inhibs
## 2B: UMAP plot for Excit
## 2C: Vln for Inhib markers
## 2D: Vln for Excit markers
## 2E: Composition for Inhibs
## 2F: Composition for Excit

tempinhib <- inhib
tempinhib@meta.data$Annotations <-stringr::str_wrap(tempinhib@meta.data$Annotations, 15)
p2a <- DimPlot(tempinhib, pt.size=2, label=T,repel=T,label.size=4,cols=c("Excitatory\nNeurons 1"="#f8b6ff", "Excitatory\nNeurons 2"="#f090fa", "Excitatory\nNeurons 3"="#fda4ff","Excitatory\nNeurons 4"="#d0a4ff","Excitatory\nNeurons 5"="#ff69b4","PV Inhibitory\nNeurons"="#72b2f4","SST Inhibitory\nNeurons"="#348feb","Unclassified\nNeurons 1"="#ffe599","Unclassified\nNeurons 2"="#ffd966","VIP Inhibitory\nNeurons"="#1c51e6","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a"), group.by='Annotations') + NoLegend()+ ggtitle(NULL)

inhib_genes <- c('Sox5','Grin3a','Vip','Erbb4','Adarb2','Gad1')
p2c <- VlnPlot(inhib, features=c(inhib_genes), stack=TRUE, sort=FALSE, flip=TRUE,group.by='Annotations',cols=c("Excitatory Neurons 1"="#f8b6ff", "Excitatory Neurons 2"="#f090fa", "Excitatory Neurons 3"="#fda4ff","Excitatory Neurons 4"="#d0a4ff","Excitatory Neurons 5"="#ff69b4","PV Inhibitory Neurons"="#72b2f4","SST Inhibitory Neurons"="#348feb","Unclassified Neurons 1"="#ffe599","Unclassified Neurons 2"="#ffd966","VIP Inhibitory Neurons"="#1c51e6","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a"), fill.by='ident') + theme(legend.position='none')+ labs(title='',x='') + theme(text=element_text(size=15),axis.text.x =element_text(size=9),axis.title.x = element_blank())+scale_x_discrete(labels=function(x) str_wrap(x, width = 15))

tempexcit <- excit
tempexcit@meta.data$Annotations <-stringr::str_wrap(tempexcit@meta.data$Annotations, 15)
p2b <- DimPlot(tempexcit, pt.size=2, label=T,repel=T,label.size=4,cols=c("Excitatory\nNeurons 1"="#f8b6ff", "Excitatory\nNeurons 2"="#f090fa", "Excitatory\nNeurons 3"="#fda4ff","Excitatory\nNeurons 4"="#d0a4ff","Excitatory\nNeurons 5"="#ff69b4","PV Inhibitory\nNeurons"="#72b2f4","SST Inhibitory\nNeurons"="#348feb","Unclassified\nNeurons 1"="#ffe599","Unclassified\nNeurons 2"="#ffd966","VIP Inhibitory\nNeurons"="#1c51e6","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a"), group.by='Annotations') + NoLegend()+ ggtitle(NULL)

excit_genes <- c('Rorb','Foxp2','Cux1','Garnl3','Chrm3','Grm8')
ordered_level <- c('Excitatory Neurons 1','Excitatory Neurons 2','Excitatory Neurons 3','Excitatory Neurons 4','Excitatory Neurons 5')
excit@meta.data$Annotations <- factor(x=excit@meta.data$Annotations,levels=ordered_level)
p2d <- VlnPlot(excit, features=c(excit_genes), stack=TRUE, sort=FALSE, flip=TRUE,group.by='Annotations',cols=c("Excitatory Neurons 1"="#f8b6ff", "Excitatory Neurons 2"="#f090fa", "Excitatory Neurons 3"="#fda4ff","Excitatory Neurons 4"="#d0a4ff","Excitatory Neurons 5"="#ff69b4","PV Inhibitory\nNeurons"="#72b2f4","SST Inhibitory\nNeurons"="#348feb","Unclassified Neurons 1"="#ffe599","Unclassified Neurons 2"="#ffd966","VIP Inhibitory\nNeurons"="#1c51e6","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a"), fill.by='ident') + theme(legend.position='none') + labs(title='',x='') + theme(text=element_text(size=15),axis.text.x =element_text(size=9),axis.title.x = element_blank())+scale_x_discrete(labels=function(x) str_wrap(x, width = 15))

Idents(inhib) <- 'Annotations'
counts <- table(Idents(inhib), inhib$samp)
counts <- prop.table(counts, margin=2)*100
#Organize the dataframe for easy plotting
df <- as.data.frame.matrix(counts) 
df$cluster <- rownames(df)
df <- df %>% gather(., treatment, props, Control:Control, factor_key=T) 
group.colors <- c("Excitatory\nNeurons 1"="#f8b6ff", "Excitatory\nNeurons 2"="#f090fa", "Excitatory\nNeurons 3"="#fda4ff","Excitatory\nNeurons 4"="#d0a4ff","Excitatory Neurons\n5"="#ff69b4","PV\nInhibitory\nNeurons"="#72b2f4","SST\nInhibitory\nNeurons"="#348feb","Unclassified Neurons 1"="#ffe599","Unclassified Neurons 2"="#ffd966","VIP\nInhibitory\nNeurons"="#1c51e6","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a")
df$cluster <- stringr::str_wrap(df$cluster, 8)
p2e <- ggplot(df, aes(x=treatment, y=props, fill=cluster, label=cluster)) + geom_bar(stat='identity', color='black') + theme_minimal() + labs(title='',x='',y='Percentage of cells', fill='Cluster:') + theme(text=element_text(size=15), axis.text.x = element_text(angle = 315, size=0)) + scale_fill_manual(values=group.colors,labels = function(x) str_wrap(x, width = 15)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
panel.background = element_blank(), axis.line = element_line(colour = "black"))+ NoLegend()+
  geom_text(size = 4, position = position_stack(vjust = 0.5))

Idents(excit) <- 'Annotations'
counts <- table(Idents(excit), excit$samp)
counts <- prop.table(counts, margin=2)*100
#Organize the dataframe for easy plotting
df <- as.data.frame.matrix(counts) 
df$cluster <- rownames(df)
df <- df %>% gather(., treatment, props, Control:Control, factor_key=T) 
group.colors <- c("Excitatory\nNeurons 1"="#f8b6ff", "Excitatory\nNeurons 2"="#f090fa", "Excitatory\nNeurons 3"="#fda4ff","Excitatory\nNeurons 4"="#d0a4ff","Excitatory\nNeurons 5"="#ff69b4","PV Inhibitory Neurons"="#72b2f4","SST Inhibitory Neurons"="#348feb","Unclassified Neurons 1"="#ffe599","Unclassified Neurons 2"="#ffd966","VIP Inhibitory Neurons"="#1c51e6","Astrocytes"="#0ad1c6","OPCs"="#73c152","Microglia"="#f5c19d","Excitatory Neurons 6"="#efd68b","Excitatory Neurons 7"="#b1c37c","Oligodendrocytes"="#59c52a")
df$cluster <- stringr::str_wrap(df$cluster, 15)
p2f <- ggplot(df, aes(x=treatment, y=props, fill=cluster, label=cluster)) + geom_bar(stat='identity', color='black') + theme_minimal() + labs(title='',x='',y='Percentage of cells', fill='Cluster:') + theme(text=element_text(size=15), axis.text.x = element_text(angle = 315, size=0)) + scale_fill_manual(values=group.colors,labels = function(x) str_wrap(x, width = 15)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
panel.background = element_blank(), axis.line = element_line(colour = "black"))+ NoLegend()+
  geom_text(size = 4, position = position_stack(vjust = 0.5))


title <- ggdraw() + 
  draw_label(
    "Figure 2",
    fontface = 'bold',
    x = 0,
    hjust = 0,
    size=20
  ) +
  theme(
    # add margin on the left of the drawing canvas,
    # so title is aligned with left edge of first plot
    plot.margin = margin(0, 0, 0, 7)
  )

topplots <- cowplot::plot_grid(p2a, p2b, ncol=2, rel_widths=c(0.5,0.5), labels=c('A','B'))

leftplots <- cowplot::plot_grid(
  cowplot::plot_grid(NULL,p2c,NULL,ncol=3, rel_widths = c(0.1, 0.8, 0.1)),
  cowplot::plot_grid(NULL,p2e,NULL,ncol=3, rel_widths = c(0.1, 0.8, 0.1)),
  ncol=2, labels=c('C'), 
  rel_widths=c(0.6,0.4)
)
rightplots <- cowplot::plot_grid(
  cowplot::plot_grid(NULL,p2d,NULL,ncol=3, rel_widths = c(0.1, 0.8, 0.1)),
  cowplot::plot_grid(NULL,p2f,NULL,ncol=3, rel_widths = c(0.1, 0.8, 0.1)),
  ncol=2, labels=c('D'), 
  rel_widths=c(0.6,0.4)
)

botplots <- cowplot::plot_grid(
  leftplots, rightplots, ncol=2
)

f2 <- cowplot::plot_grid(title, topplots, botplots, ncol=1, rel_heights=c(0.1,1,1))

###########################################################
# Figure 3
## 3A: UMAP plot for Astros
## 3B: Composition
## 3C: Markers

Idents(astros) <- 'seurat_clusters'
astros <- StashIdent(astros, save.name='seurat_original_clusters')

astros <- RenameIdents(astros, "0"="Astrocyte\nSubType 1")
astros <- RenameIdents(astros, "1"="Astrocyte\nSubType 2")
astros <- RenameIdents(astros, "2"="Astrocyte\nSubType 3")
astros <- RenameIdents(astros, "3"="Astrocyte\nSubType 4")

astros[['AstroAnnotations']] <- Idents(object=astros)

p3a <- DimPlot(astros, pt.size=2, label=T,label.size=3.5, group.by='AstroAnnotations', cols = c("Astrocyte\nSubType 1"="#0ad1c6", "Astrocyte\nSubType 2"="#32f5ea","Astrocyte\nSubType 3"="#9dece8","Astrocyte\nSubType 4"="#078881"), repel=T) + NoLegend()+ ggtitle(NULL)


Idents(astros) <- 'seurat_clusters'
astros <- RenameIdents(astros, "0"="AST 1")
astros <- RenameIdents(astros, "1"="AST 2")
astros <- RenameIdents(astros, "2"="AST 3")
astros <- RenameIdents(astros, "3"="AST 4")
astros[['AstroAnnotations2']] <- Idents(object=astros)


Idents(astros) <- 'AstroAnnotations2'
counts <- table(Idents(astros), astros$samp)
counts <- prop.table(counts, margin=2)*100
#Organize the dataframe for easy plotting
df <- as.data.frame.matrix(counts) 
df$cluster <- rownames(df)
df <- df %>% gather(., treatment, props, Control:Control, factor_key=T) 
group.colors2 <- c("AST 1"="#0ad1c6", "AST 2"="#32f5ea","AST 3"="#9dece8","AST 4"="#078881")
p3b <- ggplot(df, aes(x=treatment, y=props, fill=cluster, label=cluster)) + geom_bar(stat='identity', color='black') + theme_minimal() + labs(title='',x='',y='Percentage of cells', fill='Cluster:') + theme(text=element_text(size=15), axis.text.x = element_text(angle = 315, size=0)) + scale_fill_manual(values=group.colors2,labels = function(x) str_wrap(x, width = 15)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
panel.background = element_blank(), axis.line = element_line(colour = "black"))+ NoLegend()+
  geom_text(size = 2, position = position_stack(vjust = 0.5))

astro_genes <- c('Sox6','Nckap5','Kcnip4','Nrg3','Fn1','Tek','Syp','Celsr2')
ordered_level <- c("Astrocyte\nSubType 1","Astrocyte\nSubType 2","Astrocyte\nSubType 3","Astrocyte\nSubType 4")
astros@meta.data$AstroAnnotations <- factor(x=astros@meta.data$AstroAnnotations,levels=ordered_level)
p3c <- VlnPlot(astros, features=c(astro_genes), stack=TRUE, sort=FALSE, flip=TRUE,group.by='AstroAnnotations',cols=c("Astrocyte\nSubType 1"="#0ad1c6", "Astrocyte\nSubType 2"="#32f5ea","Astrocyte\nSubType 3"="#9dece8","Astrocyte\nSubType 4"="#078881"), fill.by='ident') + theme(legend.position='none') + labs(title='',x='') + theme(text=element_text(size=15),axis.text.x =element_text(size=9),axis.title.x = element_blank())+scale_x_discrete(labels=function(x) str_wrap(x, width = 15))
  

title <- ggdraw() + 
  draw_label(
    "Figure 3",
    fontface = 'bold',
    x = 0,
    hjust = 0,
    size=20
  ) +
  theme(
    # add margin on the left of the drawing canvas,
    # so title is aligned with left edge of first plot
    plot.margin = margin(0, 0, 0, 7)
  )


botplots <- cowplot::plot_grid(
  cowplot::plot_grid(NULL,p3c,NULL,ncol=3, rel_widths = c(0.1, 0.8, 0.1)),
  cowplot::plot_grid(NULL,p3b,NULL,ncol=3, rel_widths = c(0.1, 0.8, 0.1)),
  ncol=2, labels=c('B'), 
  rel_widths=c(0.6,0.4)
)

plots <- cowplot::plot_grid(cowplot::plot_grid(NULL,p3a,NULL,ncol=3, rel_widths = c(0.1, 0.8, 0.1),labels=c("A")), botplots, ncol=1, rel_heights=c(1,1))
f3 <- cowplot::plot_grid(title, plots, ncol=1, rel_heights=c(0.1,1))

f3plot <- cowplot::plot_grid(p3a, p3c,p3b, labels=c('A','B'), ncol=3, rel_widths=c(0.8, 0.6, 0.4)) 


###########################################################
# Figure 4
## 4A: Astros Upset (maybe venn?)
## 4B: Bubble plot

clust0 <- read.csv('./ORA_astros/GO/BP/cluster_0_GO_BP_ORA.csv', header=T, row.names = 1,stringsAsFactors = FALSE)
clust0$Cluster <- c('Astrocyte SubType 1')

clust1 <- read.csv('./ORA_astros/GO/BP/cluster_1_GO_BP_ORA.csv', header=T, row.names = 1,stringsAsFactors = FALSE)
clust1$Cluster <- c('Astrocyte SubType 2')

clust2 <- read.csv('./ORA_astros/GO/BP/cluster_2_GO_BP_ORA.csv', header=T, row.names = 1,stringsAsFactors = FALSE)
clust2$Cluster <- c('Astrocyte SubType 3')

clust3 <- read.csv('./ORA_astros/GO/BP/cluster_3_GO_BP_ORA.csv', header=T, row.names = 1)
clust3$Cluster <- c('Astrocyte SubType 4')

merged <- bind_rows(clust0, clust1, clust2, clust3)
golist <- c('synapse assembly','regulation of synapse organization','cell junction assembly','regulation of synapse structure or activity','signal release from synapse','neurotransmitter secretion','vesicle-mediated transport in synapse','synaptic vesicle cycle','regulation of neurotransmitter transport','synaptic transmission, glutamatergic','regulation of synaptic plasticity','learning or memory','cognition','axonogenesis','regulation of myelination','oligodendrocyte differentiation','vasculogenesis','regulation of angiogenesis','regulation of voltage-gated calcium channel activity','synaptic membrane adhesion','presynaptic endocytosis','synaptic vesicle recycling')

merged <- merged[merged$Description %in% golist,]

p4b <- ggplot(merged, aes(x=Cluster, y=Description, size=Count,color=p.adjust, group=Cluster)) + geom_point(alpha=0.8) + theme_classic()+scale_color_gradient(low = "red2",  high = "mediumblue", space = "Lab")+scale_size(range = c(2, 8)) + scale_y_discrete(labels=function(x) str_wrap(x, width = 25))+ scale_x_discrete(labels=function(x) str_wrap(x, width = 10))+ theme(axis.title.x = element_blank()) + ylab('GO Terms')

library(UpSetR)
upsets <- list()
upsets[['Astrocyte\nSubType 1']] <- clust0$Description
upsets[['Astrocyte\nSubType 2']] <- clust1$Description
upsets[['Astrocyte\nSubType 3']] <- clust2$Description
upsets[['Astrocyte\nSubType 4']] <- clust3$Description

p4a <- upset(fromList(upsets), nsets=15, order.by='freq',text.scale=c(2,2,2,2,2,2),
     point.size=6,
     line.size=4,
     mainbar.y.label='Significant GO pathway intersections', sets.x.label='Significant GO pathways')

library(VennDiagram)
venn <- venn.diagram(
  upsets,
  fill=c("#0ad1c6","#32f5ea","#9dece8","#078881"),
  lwd=1,
  lty=1,
  cat.cex=1,
  cex=1.5,
  alpha=1,
  filename=NULL
)

title <- ggdraw() + 
  draw_label(
    "Figure 4",
    fontface = 'bold',
    x = 0,
    hjust = 0,
    size=20
  ) +
  theme(
    # add margin on the left of the drawing canvas,
    # so title is aligned with left edge of first plot
    plot.margin = margin(0, 0, 0, 7)
  )

plots <- cowplot::plot_grid(venn,p4b, ncol=2, rel_widths=c(0.5,0.5), labels = c("A","B"))
f4 <- cowplot::plot_grid(title, plots, ncol=1, rel_heights=c(0.1,1))

pdf('./figure1.pdf',height=8, width=12)
print(f1)
dev.off()
pdf('./figure2.pdf',height=8, width=12)
print(f2)
dev.off()
pdf('./figure3.pdf',height=8, width=6)
print(f3)
dev.off()
pdf('./figure3_horizontal.pdf',height=4, width=20)
print(f3plot)
dev.off()
pdf('./figure4.pdf',height=8, width=12)
print(f4)
dev.off()

f1

f2

f3

f4

sessionInfo()
R version 4.2.2 (2022-10-31)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 22.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] grid      stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] VennDiagram_1.7.3           futile.logger_1.4.3         UpSetR_1.4.0               
 [4] enrichplot_1.18.3           msigdbr_7.5.1               clusterProfiler_4.6.2      
 [7] PCAtools_2.5.15             EnhancedVolcano_1.13.2      edgeR_3.40.2               
[10] limma_3.54.2                DESeq2_1.38.3               ComplexHeatmap_2.14.0      
[13] pheatmap_1.0.12             scater_1.26.1               scuttle_1.8.4              
[16] apeglm_1.20.0               SoupX_1.6.2                 DoubletFinder_2.0.3        
[19] SingleR_2.0.0               slingshot_2.6.0             TrajectoryUtils_1.6.0      
[22] princurve_2.1.6             velocyto.R_0.6              monocle3_1.3.1             
[25] VAM_1.0.0                   MASS_7.3-58.1               glmGamPoi_1.10.2           
[28] scCustomize_1.1.1           DropletQC_0.0.0.9000        scDataviz_1.8.0            
[31] dittoSeq_1.10.0             SeuratWrappers_0.3.1        scRNAseq_2.12.0            
[34] SingleCellExperiment_1.20.1 SummarizedExperiment_1.28.0 Biobase_2.58.0             
[37] GenomicRanges_1.50.2        GenomeInfoDb_1.34.9         IRanges_2.32.0             
[40] MatrixGenerics_1.10.0       matrixStats_0.63.0          S4Vectors_0.36.2           
[43] BiocGenerics_0.44.0         SeuratObject_4.1.3          Seurat_4.3.0               
[46] DT_0.27                     Matrix.utils_0.9.8          clustree_0.5.0             
[49] ggraph_2.1.0                magrittr_2.0.3              patchwork_1.1.2            
[52] RColorBrewer_1.1-3          reshape2_1.4.4              Matrix_1.5-1               
[55] ggpubr_0.6.0                ggrepel_0.9.3               ggalt_0.4.0                
[58] cowplot_1.1.1               hdf5r_1.3.8                 lubridate_1.9.2            
[61] forcats_1.0.0               stringr_1.5.0               dplyr_1.1.1                
[64] purrr_1.0.1                 readr_2.1.4                 tidyr_1.3.0                
[67] tibble_3.2.0                ggplot2_3.4.1               tidyverse_2.0.0            
[70] knitr_1.42                 

loaded via a namespace (and not attached):
  [1] terra_1.7-18                  graphlayouts_0.8.4            pbapply_1.7-0                
  [4] lattice_0.20-45               vctrs_0.6.1                   mgcv_1.8-41                  
  [7] flowCore_2.10.0               blob_1.2.3                    survival_3.4-0               
 [10] spatstat.data_3.0-1           later_1.3.0                   nloptr_2.0.3                 
 [13] DBI_1.1.3                     R.utils_2.12.2                rappdirs_0.3.3               
 [16] uwot_0.1.14                   dqrng_0.3.0                   zlibbioc_1.44.0              
 [19] pcaMethods_1.90.0             htmlwidgets_1.6.1             mvtnorm_1.1-3                
 [22] GlobalOptions_0.1.2           future_1.32.0                 leiden_0.4.3                 
 [25] parallel_4.2.2                irlba_2.3.5.1                 tidygraph_1.2.3              
 [28] Rcpp_1.0.10                   KernSmooth_2.23-20            promises_1.2.0.1             
 [31] DelayedArray_0.24.0           RSpectra_0.16-1               fastmatch_1.1-3              
 [34] digest_0.6.31                 png_0.1-8                     sctransform_0.3.5            
 [37] scatterpie_0.1.8              DOSE_3.24.2                   GO.db_3.16.0                 
 [40] pkgconfig_2.0.3               spatstat.random_3.1-4         DelayedMatrixStats_1.20.0    
 [43] ggbeeswarm_0.7.1              iterators_1.0.14              minqa_1.2.5                  
 [46] emdbook_1.3.12                reticulate_1.28               circlize_0.4.15              
 [49] beeswarm_0.4.0                GetoptLong_1.0.5              xfun_0.37                    
 [52] bslib_0.4.2                   zoo_1.8-11                    tidyselect_1.2.0             
 [55] ica_1.0-3                     gson_0.1.0                    viridisLite_0.4.1            
 [58] rtracklayer_1.58.0            rlang_1.1.0                   jquerylib_0.1.4              
 [61] glue_1.6.2                    ensembldb_2.22.0              lambda.r_1.2.4               
 [64] umap_0.2.10.0                 RProtoBufLib_2.10.0           ggsignif_0.6.4               
 [67] labeling_0.4.2                httpuv_1.6.9                  Rttf2pt1_1.3.12              
 [70] BiocNeighbors_1.16.0          grr_0.9.5                     annotate_1.76.0              
 [73] jsonlite_1.8.4                XVector_0.38.0                bit_4.0.5                    
 [76] mime_0.12                     gridExtra_2.3                 Rsamtools_2.14.0             
 [79] stringi_1.7.12                spatstat.sparse_3.0-1         scattermore_0.8              
 [82] spatstat.explore_3.1-0        yulab.utils_0.0.6             bitops_1.0-7                 
 [85] cli_3.6.0                     maps_3.4.1                    RSQLite_2.3.0                
 [88] data.table_1.14.8             timechange_0.2.0              rstudioapi_0.14              
 [91] GenomicAlignments_1.34.1      nlme_3.1-160                  qvalue_2.30.0                
 [94] ggprism_1.0.4                 locfit_1.5-9.7                janitor_2.2.0                
 [97] listenv_0.9.0                 miniUI_0.1.1.1                gridGraphics_0.5-1           
[100] R.oo_1.25.0                   dbplyr_2.3.1                  lifecycle_1.0.3              
[103] ExperimentHub_2.6.0           munsell_0.5.0                 R.methodsS3_1.8.2            
[106] codetools_0.2-18              coda_0.19-4                   vipor_0.4.5                  
[109] lmtest_0.9-40                 xtable_1.8-4                  ROCR_1.0-11                  
[112] formatR_1.14                  BiocManager_1.30.20           abind_1.4-5                  
[115] farver_2.1.1                  parallelly_1.35.0             AnnotationHub_3.6.0          
[118] RANN_2.6.1                    aplot_0.1.10                  askpass_1.1                  
[121] ggtree_3.6.2                  BiocIO_1.8.0                  RcppAnnoy_0.0.20             
[124] goftest_1.2-3                 futile.options_1.0.1          cluster_2.1.4                
[127] future.apply_1.10.0           extrafontdb_1.0               tidytree_0.4.2               
[130] ellipsis_0.3.2                prettyunits_1.1.1             ggridges_0.5.4               
[133] mclust_6.0.0                  igraph_1.4.1                  fgsea_1.24.0                 
[136] remotes_2.4.2                 paletteer_1.5.0               spatstat.utils_3.0-2         
[139] org.Rn.eg.db_3.16.0           htmltools_0.5.4               BiocFileCache_2.6.1          
[142] yaml_2.3.7                    GenomicFeatures_1.50.4        utf8_1.2.3                   
[145] plotly_4.10.1                 interactiveDisplayBase_1.36.0 XML_3.99-0.13                
[148] withr_2.5.0                   fitdistrplus_1.1-8            BiocParallel_1.32.6          
[151] bit64_4.0.5                   foreach_1.5.2                 ProtGenerics_1.30.0          
[154] Biostrings_2.66.0             GOSemSim_2.24.0               progressr_0.13.0             
[157] cytolib_2.10.1                rsvd_1.0.5                    ScaledMatrix_1.6.0           
[160] memoise_2.0.1                 evaluate_0.20                 geneplotter_1.76.0           
[163] tzdb_0.3.0                    extrafont_0.19                curl_5.0.0                   
[166] fansi_1.0.4                   tensor_1.5                    cachem_1.0.7                 
[169] HDO.db_0.99.1                 deldir_1.0-6                  babelgene_22.9               
[172] proj4_1.0-12                  rjson_0.2.21                  rstatix_0.7.2                
[175] clue_0.3-64                   tools_4.2.2                   sass_0.4.5                   
[178] RCurl_1.98-1.10               ape_5.7-1                     car_3.1-2                    
[181] ggplotify_0.1.0               xml2_1.3.3                    httr_1.4.5                   
[184] rmarkdown_2.20                boot_1.3-28                   globals_0.16.2               
[187] R6_2.5.1                      AnnotationFilter_1.22.0       treeio_1.22.0                
[190] progress_1.2.2                KEGGREST_1.38.0               shape_1.4.6                  
[193] beachmat_2.14.0               corrplot_0.92                 BiocVersion_3.16.0           
[196] rematch2_2.1.2                BiocSingular_1.14.0           ggrastr_1.0.1                
[199] splines_4.2.2                 snakecase_0.11.0              carData_3.0-5                
[202] ggfun_0.0.9                   colorspace_2.1-0              generics_0.1.3               
[205] pillar_1.8.1                  tweenr_2.0.2                  sp_1.6-0                     
[208] GenomeInfoDbData_1.2.9        plyr_1.8.8                    gtable_0.3.1                 
[211] bdsmatrix_1.3-6               restfulr_0.0.15               shadowtext_0.1.2             
[214] biomaRt_2.54.1                fastmap_1.1.1                 crosstalk_1.2.0              
[217] doParallel_1.0.17             AnnotationDbi_1.60.2          broom_1.0.4                  
[220] openssl_2.0.6                 scales_1.2.1                  filelock_1.0.2               
[223] backports_1.4.1               lme4_1.1-32                   hms_1.1.2                    
[226] ggforce_0.4.1                 Rtsne_0.16                    shiny_1.7.4                  
[229] ash_1.0-15                    polyclip_1.10-4               numDeriv_2016.8-1.1          
[232] bbmle_1.0.25                  lazyeval_0.2.2                crayon_1.5.2                 
[235] downloader_0.4                sparseMatrixStats_1.10.0      viridis_0.6.2                
[238] compiler_4.2.2                spatstat.geom_3.1-0          
LS0tCnRpdGxlOiAic2NSTkEgZXhwZXJpbWVudCByZXBvcnQtIEV4cGVyaW1lbnQgZm9yIEF5c2VndWwiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyMgRXhwZXJpbWVudAoKVGhpcyBleHBlcmltZW50IHdhcyBkZXNpZ25lZCBhcyBhIGZpcnN0IHN0YWdlIHByb2plY3QgdG8gZXZhbHVhdGUgUGFyc2Ugc25STkEgcHJlcHMgd2l0aGluIHRoZSBhdWRpdG9yeSBjb3J0ZXguIAoKQWxsIGV4ZWN1dGlvbiBpcyBwZXJmb3JtZWQgaW4gY29udGFpbmVyaXplZCBlbnZpcm9ubWVudCBhbmQgcmVjb3JkZWQgaW4gbWFya2Rvd24gZm9yIHJlcHJvZHVjaWJpbGl0eS4gVGhlIGNvbnRhaW5lciBjYW4gYmUgbGF1bmNoZWQgd2l0aCBgZG9ja2VyIHJ1biAtLXJtIDg3ODc6ODc4NyAtZSBQQVNTV09SRD0kUEFTUyAtdiAkUFdEOi9ob21lL3JzdHVkaW8gYWxlbWVuemUvYWJyZnNldXJhdGAsIGFuZCBhY2Nlc3NlZCB3aXRoIFtsb2NhbGhvc3Q6ODc4N10oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1kUXc0dzlXZ1hjUSkKCmBgYHtyIExpYnJhcmllcywgIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojQmFzaWMgbWFuaXB1bGF0aW9ucwpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaGRmNXIpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnYWx0KQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShwdXJycikKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KGNsdXN0cmVlKQpsaWJyYXJ5KE1hdHJpeC51dGlscykKbGlicmFyeShEVCkKCiNzY1JOQSBjb3JlIHBhY2thZ2VzCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KFM0VmVjdG9ycykKbGlicmFyeShzY1JOQXNlcSkKbGlicmFyeShTZXVyYXRXcmFwcGVycykKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShkaXR0b1NlcSkKbGlicmFyeShzY0RhdGF2aXopCmxpYnJhcnkoRHJvcGxldFFDKQpsaWJyYXJ5KHNjQ3VzdG9taXplKQpsaWJyYXJ5KGdsbUdhbVBvaSkKbGlicmFyeShWQU0pCgoKI3NjUk5BIHZlbG9jaXR5LCBwc2V1ZG90aW1lLCBhbm5vdGF0aW9ucywgZG91YmxldHMKbGlicmFyeShtb25vY2xlMykKbGlicmFyeSh2ZWxvY3l0by5SKQpsaWJyYXJ5KHNsaW5nc2hvdCkKbGlicmFyeShTaW5nbGVSKQpsaWJyYXJ5KERvdWJsZXRGaW5kZXIpCmxpYnJhcnkoU291cFgpCgojT3RoZXIgcmFuZG9tIHBhY2thZ2VzIGZvciBwc2V1ZG9idWxrIG9yIHRoaW5ncyBJIGxpa2UgdG8gdXNlCmxpYnJhcnkoYXBlZ2xtKQpsaWJyYXJ5KHNjYXRlcikKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZWRnZVIpCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpsaWJyYXJ5KFBDQXRvb2xzKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZT0xMDAwMDAqMTAyNF4yKQppbnN0YWxsLnBhY2thZ2VzKCdVcFNldFInKQppbnN0YWxsLnBhY2thZ2VzKCdWZW5uRGlhZ3JhbScpCmBgYAoKIyMjIFNhbXBsZSBpbmdyZXNzCgpUbyBsb2FkIHRoZSBzYW1wbGVzIGludG8gUiwgd2UgZmlyc3Qgc3RhcnQgd2l0aCB0aGUgcmF3IG1hdHJpeCBmaWxlcyBmcm9tIHRoZSBwYXJzZSBwaXBlbGluZS4gSSAqKnN0cm9uZ2x5KiogcmVjb21tZW5kIHN0YXJ0aW5nIGZyb20gdGhlIHJhdyBtYXRyaWNlcy4gUmF3IG1hdHJpY2VzIHdpbGwgYWxsb3cgeW91IHRvIG1vcmUgYWdub3N0aWNhbGx5IGZpbHRlciB3aGVuIGRvaW5nIGNyb3NzIHBsYXRmb3JtIHN0dWRpZXMsIHBlcmZvcm0gc291cCByZWR1Y3Rpb25zLCBhbmQgY2FuIHJlZHVjZSBjZWxsIGNhbGxlcnMgYmlhcyBhZ2FpbnN0IGdyYW51bG9jeXRlIGNlbGwgY2FsbGluZyAobm90IHJlbGV2YW50IGhlcmUgYnV0IHN0aWxsKS4gV2Ugc3RhcnQgd2l0aCB0aGUgZnVsbCBjb2xsZWN0aW9uIG9mIGRyb3BsZXRzL2VuY2Fwc3VsYXRlZCBudWNsZWksIGFuZCB0aGlzIHdpbGwgYmUgcGVyZm9ybWVkIHNhbXBsZS1ieS1zYW1wbGUgZm9yIGluc3RydWN0aW9uYWwgZWFzZS4gYW5kIGFueSBtaW5vciB0d2Vha3Mgd2UgbmVlZCB0byBtYWtlIGZvciBzaW5nbGUgc2FtcGxlcy4gCgpgYGB7ciBJbmdyZXNzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIscmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxNiwgZmlnLndpZHRoID0gMTB9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpjb250cm9sLnJhdyA8LSBSZWFkUGFyc2VCaW8oJy4vaW5wdXRfZGF0YS9Db250cm9sLy9ER0VfdW5maWx0ZXJlZC8nKQpjb250cm9sIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb250cm9sLnJhdywgbWluLmZlYXR1cmVzPTIpCmNvdW50cyA8LSBHZXRBc3NheURhdGEob2JqZWN0ID0gY29udHJvbCwgc2xvdCA9ICJjb3VudHMiKQpub256ZXJvIDwtIGNvdW50cyA+IDAKa2VlcF9nZW5lcyA8LSBNYXRyaXg6OnJvd1N1bXMobm9uemVybykgPj0gMTAKZmlsdGVyZWRfY291bnRzIDwtIGNvdW50c1trZWVwX2dlbmVzLCBdCmNvbnRyb2wgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGZpbHRlcmVkX2NvdW50cywgbWV0YS5kYXRhPWNvbnRyb2xAbWV0YS5kYXRhLCBwcm9qZWN0PSdjb250cm9sJywgYXNzYXk9J1JOQScpCgpjb250cm9sQG1ldGEuZGF0YSRzYW1wIDwtICdDb250cm9sJwoKY29udHJvbC5yYXcgPC0gY29udHJvbApjb250cm9sW1sncGVyY2VudC5taXRvJ11dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KGNvbnRyb2wsIHBhdHRlcm49J15NdC0nKQpjb250cm9sW1sncGVyY2VudC5yaWJvJ11dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KGNvbnRyb2wscGF0dGVybj0nXlJwW3NsXVtbOmRpZ2l0Ol1dJykKY29udHJvbFtbJ3BlcmNlbnQuaGInXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoY29udHJvbCwgcGF0dGVybiA9ICJeaGJifGhiYSIpCgojTWFrZSBhIHF1aWNrIHBsb3Qgb2YgdGhlIGlucHV0IFFDCnByZWZpbHRfcGxvdCA8LSBWbG5QbG90KGNvbnRyb2wsIGZlYXR1cmVzPWMoJ25GZWF0dXJlX1JOQScsJ25Db3VudF9STkEnLCdwZXJjZW50Lm1pdG8nLCdwZXJjZW50LnJpYm8nLCdwZXJjZW50LmhiJyksIG5jb2w9MSwgZ3JvdXAuYnk9J3NhbXAnLCByYXN0ZXI9RikKCnByZWZpbHRfcGxvdApgYGAKCiMjIyBGaWx0ZXJpbmcKCkEgY3JpdGljYWwgcGFydCBvZiBkYXRhIGluZ3Jlc3MgaXMgZmlsdGVyaW5nIHRoZSBkYXRhLCBhbmQgcmVxdWlyZWQgZm9yIGFsbCB0eXBlcyBvZiBzYW1wbGVzLiBUaGUgb2xkIGFkYWdlIHN0aWxsIHN0YW5kcyBzdHJvbmctIGdhcmJhZ2UgaW4gPSBnYXJiYWdlIG91dC4gV2UgY2FuIHNlZSBqdXN0IGZyb20gYWJvdmUgdGhhdCB0aGVyZSBhcmUgYHIgbGVuZ3RoKFdoaWNoQ2VsbHMoY29udHJvbCkpYCBkcm9wbGV0cyB0b3RhbCB3aXRoIGNhcHR1cmVkIG51Y2xlb3RpZGVzLiBUaGUgb2Z0ZW4gbWFqb3JpdHkgYXJlIGp1bmsgZHJvcGxldHMgd2l0aCByYW5kb20gZmxvYXRpbmcgbnVjbGVvdGlkZXMgaW4gdGhlIHNvbHV0aW9uIChha2EgdGhlIHNvdXApLSB3aGljaCB3ZSBjYW4gZmlsdGVyIG91dC4gCgpJbiB0aGlzIGNhc2Ugd2Ugc3RhcnQgd2l0aCBhIGZhaXJseSBsaWdodCBmaWx0ZXIsIHJlbW92aW5nIHRob3NlIHdoaWNoIGFyZSBtb3JlIGxpa2VseSB0cnVlIG5vaXNlIG9yIGRlZ3JhZGVkLiAKCmBgYHtyIEZpbHRlciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLmFsaWduPSJjZW50ZXIiLHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQojIFJlbW92ZSBhbGwgZHJvcGxldHMgd2l0aCA+NzAwMCBmZWF0dXJlcywgc3Ryb25nIGxpa2VsaWhvb2Qgb2YgaG9tb3R5cGljIGRvdWJsZXRzCiMgUmVtb3ZlIGFsbCBkcm9wbGV0cyB3aXRoIDwxMDAgZmVhdHVyZXMsIGxpa2VseSBub2lzZQojIFJlbW92ZSBhbGwgZHJvcGxldHMgd2l0aCA8MTAwMCBjb3VudHMgb2YgUk5BLCBsb3cgY2FwdHVyZSBkcm9wbGV0cwojIFJlbW92ZSBhbGwgZHJvcGxldHMgd2l0aCA+MTAlIG1pdG9jaG9uZHJpYWwgY29udGVudCwgbGlrZWx5IGRlYWQvZHlpbmcgY2VsbHMKIyBSZW1vdmUgYWxsIGRyb3BsZXRzIHdpdGggPjQ1JSByaWJvc29tYWwgYW5kIDIwJSBoZW1vZ2xvYmluLSBsaWtlbHkganVzdCBtZXNzIGRyb3BsZXRzCgpjb250cm9sIDwtIHN1YnNldCAoY29udHJvbCwgc3Vic2V0PSBuRmVhdHVyZV9STkEgPCA3MDAwICYgcGVyY2VudC5taXRvIDwgMTAgJiBwZXJjZW50LnJpYm8gPCA0NSAmIG5Db3VudF9STkEgPiAxMDAwICYgbkZlYXR1cmVfUk5BID4gMTAwICYgcGVyY2VudC5oYiA8IDIwKQoKI01ha2UgYSBxdWljayBwbG90IG9mIHRoZSBpbnB1dCBRQwpwb3N0ZmlsdF9wbG90IDwtIFZsblBsb3QoY29udHJvbCwgZmVhdHVyZXM9YygnbkZlYXR1cmVfUk5BJywnbkNvdW50X1JOQScsJ3BlcmNlbnQubWl0bycsJ3BlcmNlbnQucmlibycsJ3BlcmNlbnQuaGInKSwgbmNvbD0xLCBncm91cC5ieT0nc2FtcCcscmFzdGVyPUYpCgpwb3N0ZmlsdF9wbG90CmBgYAoKQXQgdGhlIGVuZCBvZiB0aGUgZmlsdGVyaW5nIHN0YWdlcywgd2Ugbm93IGhhdmUgYSBmaW5hbCBvZiBgciBsZW5ndGgoV2hpY2hDZWxscyhjb250cm9sKSlgIGRyb3BsZXRzIHRoYXQgd2UgY2FuIHN1cHBvcnQgYXMgY2VsbHMuIAoKIyMjIFNvdXAgcmVkdWN0aW9uCgpBcyBtZW50aW9uZWQgYmVmb3JlLCB0aGVyZSBhcmUgYW1iaWVudCBudWNsZW90aWRlcyBpbiB0aGUgc29sdXRpb24gdGhhdCBnZXQgY2FwdHVyZWQgYXMgImRyb3BsZXRzIi4gV2UgaWRlbnRpZmllZCBkcm9wbGV0cyB3ZSBkZWZpbmVkIGFzICJub2lzZSIgZWFybGllciwgc28gbm93IHdlIGNhbiBhdHRlbXB0IHRvIGNvcnJlY3QgdGhpcyBpc3N1ZS4gCgpgYGB7ciBTb3VwLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBpbmNsdWRlPUZBTFNFfQojc2V0IHVwIHNhbXBsZWxpc3QgZm9yIHRoZSBmaWx0ZXJlZCBtYXRyaWNlcwpzYW1wbGVsaXN0IDwtIGMoY29udHJvbCkKI1NldCB1cCBhIHBhcmFsbGVsIHNhbXBsZWxpc3QgZm9yIHJhdyBtYXRyaWNlcwpyYXdsaXN0cyA8LSBjKGNvbnRyb2wucmF3KQoKIyBjdXJzb3J5IGNsdXN0ZXJpbmcKY29udHJvbCA8LSBTQ1RyYW5zZm9ybShjb250cm9sLCB2YXJzLnRvLnJlZ3Jlc3M9YygncGVyY2VudC5taXRvJywncGVyY2VudC5yaWJvJyksIHZlcmJvc2U9RkFMU0UsIG1ldGhvZD0nZ2xtR2FtUG9pJykKY29udHJvbCA8LSBSdW5QQ0EoY29udHJvbCkKY29udHJvbCA8LSBSdW5VTUFQKGNvbnRyb2wsIGRpbXM9MTozMCkKY29udHJvbCA8LSBGaW5kTmVpZ2hib3JzKGNvbnRyb2wscmVkdWN0aW9uPSdwY2EnLCBkaW1zPTE6MzApCmNvbnRyb2wgPC0gRmluZENsdXN0ZXJzKGNvbnRyb2wsIHJlc29sdXRpb249MC41KQoKI0NvcnJlY3RpbmcgdGhlIHJhdyBSTkEgY291bnRzCkRlZmF1bHRBc3NheShjb250cm9sKSA8LSAnUk5BJwojZGVmaW5lIG91ciBzb3VwIGNoYW5uZWwKc291cC5jaGFubmVsIDwtIFNvdXBDaGFubmVsKGNvbnRyb2wucmF3QGFzc2F5cyRSTkFAZGF0YSwgY29udHJvbEBhc3NheXMkUk5BQGRhdGEpCiNTZXQgaXQgdG8gdGhlIGNsdXN0ZXJzCnNvdXAuY2hhbm5lbCA8LSBzZXRDbHVzdGVycyhzb3VwLmNoYW5uZWwsIHNldE5hbWVzKGNvbnRyb2xAbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycywgcm93bmFtZXMoY29udHJvbEBtZXRhLmRhdGEpKSkKc291cC5jaGFubmVsIDwtIHNldERSKHNvdXAuY2hhbm5lbCwgY29udHJvbEByZWR1Y3Rpb25zJHVtYXBAY2VsbC5lbWJlZGRpbmdzKQojRXN0aW1hdGUgY29udGFtaW5hbnQgbGV2ZWxzCnNvdXAuY2hhbm5lbCA8LSBhdXRvRXN0Q29udChzb3VwLmNoYW5uZWwpCiNBZGp1c3QgdGhlIG1hdHJpeAphZGoubWF0cml4IDwtIGFkanVzdENvdW50cyhzb3VwLmNoYW5uZWwsIHJvdW5kVG9JbnQ9VCkKICAKI1NpbmNlIHdlcmUgaW4gYSBsb29wLSB3ZSB3aWxsIGp1c3QgZGVmaW5lIHRoZSBzYW1wbGUgYXMgUyBmb3Igbm93IGFuZCBhc3NpZ24gaXQgbGF0ZXIgZm9yIGVhc2UKcyA8LSBDcmVhdGVTZXVyYXRPYmplY3QoYWRqLm1hdHJpeCkKc0BtZXRhLmRhdGEkc2FtcCA8LSBwYXN0ZSh1bmlxdWUoY29udHJvbCRzYW1wKSkKICAKI0J1dCBub3cgc2luY2Ugd2UgaGF2ZSBhIHJhdyBvYmplY3QgYWdhaW4gd2UgbmVlZCB0byByZXByb2Nlc3MKc1tbJ3BlcmNlbnQubWl0byddXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChzLCBwYXR0ZXJuPSdeTXQtJyk7CnNbWydwZXJjZW50LnJpYm8nXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQocyxwYXR0ZXJuPSdeUnBbc2xdW1s6ZGlnaXQ6XV0nKTsKc1tbJ3BlcmNlbnQuaGInXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQocywgcGF0dGVybiA9ICJeaGJifGhiYSIpCnMgPC0gc3Vic2V0IChzLCBzdWJzZXQ9IG5GZWF0dXJlX1JOQSA8IDcwMDAgJiBwZXJjZW50Lm1pdG8gPCAxMCAmIHBlcmNlbnQucmlibyA8IDQ1ICYgbkNvdW50X1JOQSA+IDEwMDAgJiBuRmVhdHVyZV9STkEgPiAxMDAgJiBwZXJjZW50LmhiIDwgMjApOwoKI1Jld3JpdGUgdGhlIGNvbnRyb2wgb2JqZWN0IGFzIHRoZSBjb3JyZWN0ZWQgb2JqZWN0CmNvbnRyb2wgPC0gcyAKYGBgCgojIyMgTm9ybWFsaXphdGlvbiBhbmQgcmVncmVzc2lvbgoKTm93IHdlIGdldCBpbnRvIG5vcm1hbGl6aW5nIHRoZSBzYW1wbGVzLCByZWdyZXNzaW5nIG5vbi12aWFibGUgZGF0YSwgYW5kIHByZXBhcmluZyB0aGUgc2FtcGxlcyBmb3IgaW50ZWdyYXRpb24gKGlmIGFwcGxpY2FibGUpLiBFYXJsaWVyIHdvcmtmbG93cyBvZiBzY1JOQSBkYXRhIHVzZWQgYSBzaW1wbGUgbG9nLXRyYW5zZm9ybWF0aW9uIGFzIGEgbm9ybWFsaXphdGlvbiBwcm9jZWR1cmUuIFRoaXMgaGFzIGJlZW4gc2hvd24gdG8gYmUgaW5zdWZmaWNpZW50IGFzIGl0IHByaW1hcmlseSBvbmx5IGNvcnJlY3RzIGZvciBkZXB0aC4gQ3VycmVudCBiZXN0IHByYWN0aWNlcyBpbmNsdWRlIHVzaW5nIHJlZ3VsYXJpemVkIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24tIHdpdGhpbiBTZXVyYXQgYmVpbmcgYXBwbGllZCBieSBTQ1RyYW5zZm9ybS4gVGhpcyBzdGVwIGFsc28gY2FuIGJlIHVzZWQgdG8gcmVncmVzcyBvdXQgdGhpbmdzIGxpa2UgbWl0b2Nob25kcmlhbCBjb250ZW50IGFuZCByaWJvc29tYWwgY29udGVudCBhcyBtYWpvciBkcml2ZXJzIG9mIGRpZmZlcmVudGlhdGlvbi0gdGhvdWdoIGFzIHdlIHNhdyBhYm92ZSBpdCBjYW4gc3RpbGwgYmUgYSBtYWpvciBmYWNldCBvZiBjbHVzdGVycy4gCgpgYGB7ciBOb3JtYWxpemF0aW9uLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTIsIGZpZy53aWR0aCA9IDEwfQpjb250cm9sIDwtIFNDVHJhbnNmb3JtKGNvbnRyb2wsIHZhcnMudG8ucmVncmVzcz1jKCdwZXJjZW50Lm1pdG8nLCdwZXJjZW50LnJpYm8nKSwgdmVyYm9zZT1GQUxTRSwgbWV0aG9kPSdnbG1HYW1Qb2knKQpjb250cm9sIDwtIFJ1blBDQShjb250cm9sKQpjb250cm9sIDwtIFJ1blVNQVAoY29udHJvbCwgZGltcz0xOjMwKQpjb250cm9sIDwtIEZpbmROZWlnaGJvcnMoY29udHJvbCwgcmVkdWN0aW9uPSdwY2EnLCBkaW1zPTE6MzApCnJlc29sdXRpb25zIDwtIGMoMC4yLCAwLjMsIDAuNCwgMC41LCAwLjYsIDAuNywwLjgsIDAuOSwxKQpjb250cm9sIDwtIEZpbmRDbHVzdGVycyhjb250cm9sLCByZXNvbHV0aW9uPWMocmVzb2x1dGlvbnMpKQoKcGxvdF9ncmlkKERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuMicsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuMycsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuNCcsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuNScsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuNicsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuNycsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuOCcsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuOScsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoY29udHJvbCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjEnLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBuY29sPTMKICAgICAgICAgICkKYGBgCgpOb3cgYnkgbG9va2luZyBhdCB0aGVzZSB3ZSBjYW4gc2VlIGEgZmFpciBiaXQgb2Ygc2VncmVnYXRpb24gd2l0aGluIHRoZSBjbHVzdGVycy4gT3ZlcmFsbCwgSSB3b3VsZCBjaG9vc2UgdGhlIDAuMiByZXNvbHV0aW9uIGFzIHRoZSBvbmUgbW9zdCBsaWtlbHkgdG8gZ2l2ZSBuaWNlIGJpb2xvZ2ljYWwgc2VwYXJhdGlvbiwgYW5kIHdlIGNhbiBwbG90IGl0IG91dC4KCmBgYHtyIFNhbXBEaW0sIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxMiwgZmlnLndpZHRoID0gMTB9CiNEZWZpbmUgdGhlIGRlZmF1bHQgcmVzb2x1dGlvbiBhbmQgcGxvdCBpdCBvdXQKRGVmYXVsdEFzc2F5KGNvbnRyb2wpIDwtICdTQ1QnCklkZW50cyhjb250cm9sKSA8LSAnU0NUX3Nubl9yZXMuMC4yJwpjb250cm9sW1snc2V1cmF0X2NsdXN0ZXJzJ11dIDwtIElkZW50cyhjb250cm9sKQoKRGltUGxvdChjb250cm9sLCBncm91cC5ieT0nc2V1cmF0X2NsdXN0ZXJzJywgbGFiZWw9VCwgcHQuc2l6ZT0xLjUsIG5jb2w9MikgKyBOb0xlZ2VuZCgpCmBgYAoKIyMjIEFubm90YXRpb24KCk5vdyBsZXRzIHRyeSB0byBhbm5vdGF0ZSB0aGVzZSBjbHVzdGVycy4gVGhpcyBjYW4gYmUgZG9uZSBiYXNlZCBvbiBhbGlnbm1lbnQgdG8gcHVibGljIGRhdGFiYXNlcyAoSENBLCBjZWxsZGV4LCBldGMpLCBsYWJlbCB0cmFuc2ZlcnMgZnJvbSBwcmV2aW91cyBwcm9qZWN0cywgb3IgbWFudWFsIGN1cmF0aW9uLiBVc3VhbGx5IHRoaXMgcmVxdWlyZXMgYSBoaWdoIGxldmVsIG9mIGRvbWFpbi1zcGVjaWZpYyBleHBlcnRpc2UgYW5kIHJlcXVpcmVzIGJpb2xvZ2ljYWwgY29sbGFib3JhdGlvbi4gU2luY2UgdGhlc2UgYXJlIGltbXVuZSBjZWxscywgd2Ugd2lsbCB1c2UgYSBxdWljayBhdXRvbWF0ZWQgdGVzdCBhbmQgc29tZSBjYW5vbmljYWwgbWFya2Vycy4gCgojIyMjIE1hbnVhbCBjdXJhdGlvbgoKIyMjIyMgQ2x1c3RlciBtYXJrZXJzCgpUaGUgZmlyc3Qgc3RlcCBpcyB1c3VhbGx5IGlkZW50aWZ5aW5nIHRoZSB0b3AgbWFya2VycyBmb3IgZWFjaCBjbHVzdGVyLiBUaGlzIHdpbGwgZ2l2ZSB5b3UgdGhlIGV4cHJlc3Npb24gY29tcGFyZWQgdG8gYWxsIG90aGVyIGNlbGxzLCBhcyB3ZWxsIGFzIHRoZSBwZXJjZW50YWdlIG9mIGNlbGxzIGluIHRoZSByZXNwZWN0aXZlIGNsdXN0ZXIvYWxsIG90aGVyIGNlbGxzIHRoYXQgZXhwcmVzcyB0aGUgZ2VuZS4gRnJvbSB0aGVyZSB5b3UgY2FuIHN0YXJ0IHRvIHRyeSBhbmQgYXNzaWduIGFubm90YXRpb25zIHRvIGVhY2guIAoKYGBge3IgQ2x1c3Rlck1hcmtlcnMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsICByZXN1bHRzPSdoaWRlJyxmaWcuYWxpZ249ImNlbnRlciJ9CklkZW50cyhjb250cm9sKSA8LSAnc2V1cmF0X2NsdXN0ZXJzJwpjb250cm9sIDwtIFByZXBTQ1RGaW5kTWFya2Vycyhjb250cm9sKQptYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKGNvbnRyb2wsIG9ubHkucG9zPVQsIG1pbi5kaWZmLnBjdCA9IDAuNCkKZGF0YXRhYmxlKG1hcmtlcnMpCmBgYAoKIyMjIyMgUGxvdHRpbmcgY2Fub25pY2FsIG1hcmtlcnMKCkxpa2V3aXNlLCB3ZSBjYW4gc2ltcGx5IHBsb3Qgb3V0IG1hcmtlcnMgd2UgZW52aXNpb24gYXMgY2Fub25pY2FsIG1hcmtlcnMgdG8gaW50ZXJyb2dhdGUgdGhlIGRhdGEgZnVydGhlciBmb3IgYW5ub3RhdGlvbnMuCgpgYGB7ciBTdGFja1Bsb3QsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxNiwgZmlnLndpZHRoID0gMTB9CmdlbmVzIDwtIGMoJ0FxcDQnLCdHZmFwJywnRmdmcjMnLCdTbGMxNGExJywnSWQzJywnR2phMScsJ0dwcjM3bDEnLCdDYW1rMmEnLCdDYWJyYTUnLCdJc2xyMicsJ0ZpbGlwMScsJ1JnczUnLCdPc3RuJywnVW5jNWInLCdWYW1wMScsJ0NhY25hMWcnLCdTbWFyY2EyJywnRmFtMTk4YicsJ1NteWQyJywnQ2Q2MycsJ0NudG5hcDUnLCdQaGxkYjInLCdBcmMnLCdUbmsyJywnU25jYicsJ0JjbDJsMScsJ0lucHA1aicsJ0lnZmJwMicsJ1NvY3MyJywnSHRyMmMnLCdTY2duJywnVXNwMzYnLCdBYmNhOCcsJ1ZpcCcsJ1JwczE1YScsJ0NkaDMnLCdQcmtjZycsJ05vczEnLCdQZW5rJywnR3BtNmEnLCdNYXBrNicsJ0Z1b20nLCdQa2lhJywnQ3BseDInLCdUcmRuJywnU2xjMTdhNicsJ0dybTInLCdQbm9jJywnU2xjNmEzJywnVGgnLCdTbGMxOGEyJywnRHJkMicsJ1NsYzMyYTEnLCdHYWQxJywnR2FkMicsJ0h0cjJjJywnT3BybTEnLCdNb2cnLCdNb2cxJywnUGRnZnJhJywnQzFxYicsJ01zbW8xJywnU3l0MTAnLCdLbGhsNCcsJ1BjZGgxNycsJ1JhbXAxJywnVHRuJywnRGJpJywnUmVsbicsJ0xoeDYnLCdUcnBjMycsJ0NocmQnLCdTbGMyNGEyJywnU2VjMTFjJywnTWt4JywnSHRyMmMnLCdOeHBoNCcsJ1N5dDYnLCdDY2snLCdOcHknLCdLcnQxNycsJ0NibG40JywnR3ByODMnLCdXZmRjMicpCgpwIDwtIFZsblBsb3QoY29udHJvbCwgZmVhdHVyZXM9YyhnZW5lcyksIHN0YWNrPVRSVUUsIHNvcnQ9VFJVRSwgZmxpcD1UUlVFLGdyb3VwLmJ5PSdzZXVyYXRfY2x1c3RlcnMnKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpCnAKYGBgCgojIyMjIyBBc3NpZ25pbmcgQW5ub3RhdGlvbnMKQmFzZWQgb24gdGhlIGNvbWJpbmF0aW9uIG9mIG1hbnVhbCBjdXJhdGlvbiBhbmQgZGlzc2VjdGlvbiBvZiBtYXJrZXIgZ2VuZXMgKHNlZSBwYXBlciBmb3IgbW9yZSBkZXRhaWxzIGFuZCB0aGUgYmlvbG9neSBvZiBpdCwgYXMgd2VsbCBhcyBjaXRhdGlvbnMgdG8gdmFyaW91cyBtYXJrZXJzIHVzZWQpLCB3ZSBhc3NpZ25lZCB0aGUgc3BlY2lmaWMgYW5ub3RhdGlvbnMgdG8gdGhlIGNsdXN0ZXJzIGFzIGJlbG93LiAKCmBgYHtyIEFubm90YXRpb25zLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQoKSWRlbnRzKGNvbnRyb2wpIDwtICdzZXVyYXRfY2x1c3RlcnMnCmNvbnRyb2wgPC0gU3Rhc2hJZGVudChjb250cm9sLCBzYXZlLm5hbWU9J3NldXJhdF9vcmlnaW5hbF9jbHVzdGVycycpCgpjb250cm9sIDwtIFJlbmFtZUlkZW50cyhjb250cm9sLCAiMCI9IkV4Y2l0YXRvcnkgTmV1cm9ucyAxIikKY29udHJvbCA8LSBSZW5hbWVJZGVudHMoY29udHJvbCwgIjEiPSJFeGNpdGF0b3J5IE5ldXJvbnMgMiIpCmNvbnRyb2wgPC0gUmVuYW1lSWRlbnRzKGNvbnRyb2wsICIyIj0iRXhjaXRhdG9yeSBOZXVyb25zIDMiKQpjb250cm9sIDwtIFJlbmFtZUlkZW50cyhjb250cm9sLCAiMyI9IkV4Y2l0YXRvcnkgTmV1cm9ucyA0IikKY29udHJvbCA8LSBSZW5hbWVJZGVudHMoY29udHJvbCwgIjQiPSJFeGNpdGF0b3J5IE5ldXJvbnMgNSIpCmNvbnRyb2wgPC0gUmVuYW1lSWRlbnRzKGNvbnRyb2wsICI1Ij0iT2xpZ29kZW5kcm9jeXRlcyIpCmNvbnRyb2wgPC0gUmVuYW1lSWRlbnRzKGNvbnRyb2wsICI2Ij0iUFYgSW5oaWJpdG9yeSBOZXVyb25zIikKY29udHJvbCA8LSBSZW5hbWVJZGVudHMoY29udHJvbCwgIjciPSJTU1QgSW5oaWJpdG9yeSBOZXVyb25zIikKY29udHJvbCA8LSBSZW5hbWVJZGVudHMoY29udHJvbCwgIjgiPSJVbmNsYXNzaWZpZWQgTmV1cm9ucyAxIikKY29udHJvbCA8LSBSZW5hbWVJZGVudHMoY29udHJvbCwgIjkiPSJWSVAgSW5oaWJpdG9yeSBOZXVyb25zIikKY29udHJvbCA8LSBSZW5hbWVJZGVudHMoY29udHJvbCwgIjEwIj0iQXN0cm9jeXRlcyIpCmNvbnRyb2wgPC0gUmVuYW1lSWRlbnRzKGNvbnRyb2wsICIxMSI9IlVuY2xhc3NpZmllZCBOZXVyb25zIDIiKQpjb250cm9sIDwtIFJlbmFtZUlkZW50cyhjb250cm9sLCAiMTIiPSJPUENzIikKY29udHJvbCA8LSBSZW5hbWVJZGVudHMoY29udHJvbCwgIjEzIj0iRXhjaXRhdG9yeSBOZXVyb25zIDciKQpjb250cm9sIDwtIFJlbmFtZUlkZW50cyhjb250cm9sLCAiMTQiPSJFeGNpdGF0b3J5IE5ldXJvbnMgNiIpCmNvbnRyb2wgPC0gUmVuYW1lSWRlbnRzKGNvbnRyb2wsICIxNSI9Ik1pY3JvZ2xpYSIpCgpjb250cm9sW1snQW5ub3RhdGlvbnMnXV0gPC0gSWRlbnRzKG9iamVjdD1jb250cm9sKQoKRGltUGxvdChjb250cm9sLCBncm91cC5ieT0nQW5ub3RhdGlvbnMnLCBsYWJlbD1ULCBwdC5zaXplPTEuNzUsIGxhYmVsLnNpemU9NSwgY29scz1jKCJFeGNpdGF0b3J5IE5ldXJvbnMgMSI9IiNmOGI2ZmYiLCAiRXhjaXRhdG9yeSBOZXVyb25zIDIiPSIjZjA5MGZhIiwgIkV4Y2l0YXRvcnkgTmV1cm9ucyAzIj0iI2ZkYTRmZiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA0Ij0iI2QwYTRmZiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA1Ij0iI2ZmNjliNCIsIlBWIEluaGliaXRvcnkgTmV1cm9ucyI9IiM3MmIyZjQiLCJTU1QgSW5oaWJpdG9yeSBOZXVyb25zIj0iIzM0OGZlYiIsIlVuY2xhc3NpZmllZCBOZXVyb25zIDEiPSIjZmZlNTk5IiwiVW5jbGFzc2lmaWVkIE5ldXJvbnMgMiI9IiNmZmQ5NjYiLCJWSVAgSW5oaWJpdG9yeSBOZXVyb25zIj0iIzgyYjZlNSIsIkFzdHJvY3l0ZXMiPSIjMGFkMWM2IiwiT1BDcyI9IiM3M2MxNTIiLCJNaWNyb2dsaWEiPSIjZjVjMTlkIiwiRXhjaXRhdG9yeSBOZXVyb25zIDYiPSIjZWZkNjhiIiwiRXhjaXRhdG9yeSBOZXVyb25zIDciPSIjYjFjMzdjIiwiT2xpZ29kZW5kcm9jeXRlcyI9IiM1OWM1MmEiKSwgcmVwZWw9VCkKYGBgCgojIyMjIyBQbG90dGluZyBtb3JlIGNhbm9uaWNhbCBtYXJrZXJzCmBgYHtyIFZsbkNhbm9uLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQpnZW5lcyA8LSBjKCdDaHJtMycsJ0N1eDInLCdVbmM1ZCcsJ0Rna2InLCdSb3JiJywnVGhzZDdhJywnQ2FtazQnLCdTZW1hM2UnLCdBZGFtdHMzJywnSWwxcmFwbDInLCdQbGN4ZDMnLCdUaHNkN2EnLCdHYXJubDMnLCdGb3hwMicsJ1pmcG0yJywnR2FkMScsJ0dhZDInLCdTbGM2YTEnLCdFeWE0JywnUmVsbicsJ0dyaW4zYScsJ0NkaDknLCdTZW1hM2MnLCdHcm04JywnU2xpdDInLCdUb3gnLCdWaXAnLCdHYWJyZzMnLCdDbnIxJywnTHlwZDYnLCdSZ3MxMicsJ0IzZ2F0MicsJ01nYXQ0YycsJ01icCcsJ01vYnAnLCdNYWcnLCdNYWwnLCdNb2cnLCdUdWJiNGEnLCdQdHByejEnLCdQZGdmcmEnLCdTb3g2JywnQXJoZ2FwMzEnLCdHcHIxNycsJ1ZjYW4nLCdTbGMxYTInLCdTbGMxYTMnLCdGYW0xMDdhJywnUzEwMGInLCdOZHJnMicsJ0Fwb2UnLCdDeDNjcjEnLCdDdHNzJywnU2xjbzJiMScsJ1pmaHgzJywnSW5wcDVkJywnRmliY2QxJywnR3ByMTYxJywnRmliY2QxJywnQ3BuZTcnLCdTY24zYicsJ054cGgxJywnT2xmbTMnLCdOZXRvMicsJ1RycGMzJykKcCA8LSBWbG5QbG90KGNvbnRyb2wsIGZlYXR1cmVzPWMoZ2VuZXMpLCBzdGFjaz1UUlVFLCBzb3J0PUZBTFNFLCBmbGlwPVRSVUUsZ3JvdXAuYnk9J0Fubm90YXRpb25zJyxjb2xzPWMoIkV4Y2l0YXRvcnkgTmV1cm9ucyAxIj0iI2Y4YjZmZiIsICJFeGNpdGF0b3J5IE5ldXJvbnMgMiI9IiNmMDkwZmEiLCAiRXhjaXRhdG9yeSBOZXVyb25zIDMiPSIjZmRhNGZmIiwiRXhjaXRhdG9yeSBOZXVyb25zIDQiPSIjZDBhNGZmIiwiRXhjaXRhdG9yeSBOZXVyb25zIDUiPSIjZmY2OWI0IiwiUFYgSW5oaWJpdG9yeSBOZXVyb25zIj0iIzcyYjJmNCIsIlNTVCBJbmhpYml0b3J5IE5ldXJvbnMiPSIjMzQ4ZmViIiwiVW5jbGFzc2lmaWVkIE5ldXJvbnMgMSI9IiNmZmU1OTkiLCJVbmNsYXNzaWZpZWQgTmV1cm9ucyAyIj0iI2ZmZDk2NiIsIlZJUCBJbmhpYml0b3J5IE5ldXJvbnMiPSIjODJiNmU1IiwiQXN0cm9jeXRlcyI9IiMwYWQxYzYiLCJPUENzIj0iIzczYzE1MiIsIk1pY3JvZ2xpYSI9IiNmNWMxOWQiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNiI9IiNlZmQ2OGIiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNyI9IiNiMWMzN2MiLCJPbGlnb2RlbmRyb2N5dGVzIj0iIzU5YzUyYSIpLCBmaWxsLmJ5PSdpZGVudCcpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdub25lJykKcApgYGAKCiMjIyBSZWNsdXN0ZXJpbmcgQXN0cm9jeXRlcwpBIHBvaW50IG9mIHBhcnRpY3VsYXIgaW50ZXJlc3QgaW4gdGhpcyBzdHVkeSB3YXMgYXN0cm9jeXRlcywgc28gdG8gZGl2ZSBmdXJ0aGVyIGludG8gdGhvc2Ugd2UgcmVjbHVzdGVyZWQganVzdCB0aGUgYXN0cm9jeXRlIGNsdXN0ZXIgdG8gYXR0ZW1wdCB0byBlbHVjaWRhdGUgdGhlIGRpdmVyc2l0eSB3aXRoaW4uIAoKYGBge3IgQXN0cm9SZWNsdXN0ZXIsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxNiwgZmlnLndpZHRoID0gMTB9CklkZW50cyhjb250cm9sKSA8LSAnc2V1cmF0X2NsdXN0ZXJzJwphc3Ryb3MgPC0gc3Vic2V0KGNvbnRyb2wsIGlkZW50cz1jKDEwKSkKRGVmYXVsdEFzc2F5KGFzdHJvcykgPC0gJ1JOQScKCmFzdHJvcyA8LSBEaWV0U2V1cmF0KGFzdHJvcywgYXNzYXlzPSdSTkEnKQphc3Ryb3MgPC0gU0NUcmFuc2Zvcm0oYXN0cm9zLCB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCdwZXJjZW50Lm1pdG8nLCdwZXJjZW50LnJpYm8nKSx2ZXJib3NlPUZBTFNFKQphc3Ryb3MgPC0gUnVuUENBKGFzdHJvcykKYXN0cm9zIDwtIFJ1blVNQVAoYXN0cm9zLCBkaW1zPTE6MzApCmFzdHJvcyA8LSBGaW5kTmVpZ2hib3JzKGFzdHJvcywgcmVkdWN0aW9uPSdwY2EnLCBkaW1zPTE6MzApCnJlc29sdXRpb25zIDwtIGMoMC4yLCAwLjMsIDAuNCwgMC41LCAwLjYsIDAuNywwLjgsIDAuOSwxKQphc3Ryb3MgPC0gRmluZENsdXN0ZXJzKGFzdHJvcywgcmVzb2x1dGlvbj1jKHJlc29sdXRpb25zKSkKCgpwbG90X2dyaWQoRGltUGxvdChhc3Ryb3MsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjInLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGFzdHJvcywgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuMycsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoYXN0cm9zLCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMC40JywgbGFiZWw9VCkgKyBOb0xlZ2VuZCgpLAogICAgICAgICAgRGltUGxvdChhc3Ryb3MsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjUnLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGFzdHJvcywgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuNicsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoYXN0cm9zLCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMC43JywgbGFiZWw9VCkgKyBOb0xlZ2VuZCgpLAogICAgICAgICAgRGltUGxvdChhc3Ryb3MsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjgnLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGFzdHJvcywgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuOScsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoYXN0cm9zLCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMScsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIG5jb2w9MwogICAgICAgICAgKQpgYGAKCmBgYHtyIEFzdHJvQ2x1c3RlcnMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxNiwgZmlnLndpZHRoID0gMTB9CkRlZmF1bHRBc3NheShhc3Ryb3MpIDwtICdTQ1QnCklkZW50cyhhc3Ryb3MpIDwtICdTQ1Rfc25uX3Jlcy4wLjUnCmFzdHJvc1tbJ3NldXJhdF9jbHVzdGVycyddXSA8LSBJZGVudHMoYXN0cm9zKQoKRGltUGxvdChhc3Ryb3MsIGdyb3VwLmJ5PSdzZXVyYXRfY2x1c3RlcnMnLCBsYWJlbD1ULCBwdC5zaXplPTEuNSwgbmNvbD0yKSArIE5vTGVnZW5kKCkKCmBgYAoKYGBge3IgQXN0cm9NYXJrZXJzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQpJZGVudHMoYXN0cm9zKSA8LSAnc2V1cmF0X2NsdXN0ZXJzJwphc3Ryb3MgPC0gUHJlcFNDVEZpbmRNYXJrZXJzKGFzdHJvcykKYXN0cm9tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKGFzdHJvcywgb25seS5wb3M9VCwgbWluLmRpZmYucGN0ID0gMC40KQoKZGF0YXRhYmxlKGFzdHJvbWFya2VycykKYGBgCgojIyMjIEFzdHJvY3l0ZSBQYXRod2F5cwpPbmNlIHdlIGhhZCBpZGVudGlmaWVkIHRoZSBtYXJrZXIgZ2VuZXMgcGVyIGFzdHJvY3l0ZSBjbHVzdGVyLCB3ZSB3YW50ZWQgdG8gdmlldyBob3cgdGhlIGRpZmZlcmVudCBwb3B1bGF0aW9ucyB3ZXJlIGRpZmZlcmVudCBmcm9tIGVhY2ggb3RoZXIgdG8gdHJ5IGFuZCBkZWxpbmVhdGUgZnVuY3Rpb24uIEZvciB0aGlzIHdlIHVzZWQgb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcyBhZ2FpbnN0IEtFR0cgYW5kIEdPIGRhdGFiYXNlcy4gCgpgYGB7ciBBc3Ryb1BhdGh3YXlzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShtc2lnZGJyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZW5yaWNocGxvdCkKCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJvcmcuUm4uZWcuZGIiKQpvcmdhbmlzbT0nb3JnLlJuLmVnLmRiJwprb3JnPSdybm8nCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmRpci5jcmVhdGUoJy4vT1JBX2FzdHJvcycpCmRpci5jcmVhdGUoJy4vT1JBX2FzdHJvcy9LRUdHJykKZGlyLmNyZWF0ZSgnLi9PUkFfYXN0cm9zL0dPJykKZGlyLmNyZWF0ZSgnLi9PUkFfYXN0cm9zL0dPL0JQJykKZGlyLmNyZWF0ZSgnLi9PUkFfYXN0cm9zL0dPL0NDJykKZGlyLmNyZWF0ZSgnLi9PUkFfYXN0cm9zL0dPL01GJykKCgpmb3IgKGkgaW4gdW5pcXVlKElkZW50cyhhc3Ryb3MpKSl7CiAgbWFya3MgPC0gRmluZE1hcmtlcnMoYXN0cm9zLCBpZGVudC4xPWksIG9ubHkucG9zPVQsIG1pbi5kaWZmLnBjdCA9IDAuMywgbG9nZmMudGhyZXNob2xkID0gMC41KQogIG1hcmtzIDwtIHN1YnNldChtYXJrcywgcF92YWxfYWRqIDwgMC4wNSkKICBnZW5lcyA8LSByb3duYW1lcyhtYXJrcykKICAKICBpZHMgPC0gYml0cihnZW5lcywgZnJvbVR5cGU9J1NZTUJPTCcsIHRvVHlwZT0nRU5UUkVaSUQnLCBPcmdEYj1vcmdhbmlzbSkKICAKICBydW4gPC0gdHJ5KHsKICAgIG9yYSA8LSBlbnJpY2hHTyhnZW5lPWlkcyRFTlRSRVpJRCwgb250PSdCUCcsIGtleVR5cGU9J0VOVFJFWklEJyxPcmdEYj1vcmdhbmlzbSkKICAgIAogICAgcGxvdCA8LSBkb3RwbG90KG9yYSwgc2hvd0NhdGVnb3J5PTEwLCBmb250LnNpemU9MTApICsgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHM9ZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGg9NDApKQogICAganBlZyhwYXN0ZSgnLi9PUkFfYXN0cm9zL0dPL0JQL2NsdXN0ZXJfJyxpLCdfR09fQlBfT1JBLmpwZycsc2VwPScnKSwgaGVpZ2h0PTgwMCwgd2lkdGg9ODAwKQogICAgcHJpbnQocGxvdCkKICAgIGRldi5vZmYoKQogICAgCiAgICB3cml0ZS5jc3Yob3JhLCBmaWxlPXBhc3RlKCcuL09SQV9hc3Ryb3MvR08vQlAvY2x1c3Rlcl8nLGksJ19HT19CUF9PUkEuY3N2JyxzZXA9JycpKQogIH0pCiAgaWYoaW5oZXJpdHMocnVuLCAndHJ5LWVycm9yJykpIHsKICAgIG5leHQKICB9CiAgcnVuIDwtIHRyeSh7CiAgICBvcmEgPC0gZW5yaWNoR08oZ2VuZT1pZHMkRU5UUkVaSUQsIG9udD0nQ0MnLCBrZXlUeXBlPSdFTlRSRVpJRCcsT3JnRGI9b3JnYW5pc20pCiAgICAKICAgIHBsb3QgPC0gZG90cGxvdChvcmEsIHNob3dDYXRlZ29yeT0xMCwgZm9udC5zaXplPTEwKSArIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzPWZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoPTQwKSkKICAgIGpwZWcocGFzdGUoJy4vT1JBX2FzdHJvcy9HTy9DQy9jbHVzdGVyXycsaSwnX0dPX0NDX09SQS5qcGcnLHNlcD0nJyksIGhlaWdodD04MDAsIHdpZHRoPTgwMCkKICAgIHByaW50KHBsb3QpCiAgICBkZXYub2ZmKCkKICAgIAogICAgd3JpdGUuY3N2KG9yYSwgZmlsZT1wYXN0ZSgnLi9PUkFfYXN0cm9zL0dPL0NDL2NsdXN0ZXJfJyxpLCdfR09fQ0NfT1JBLmNzdicsc2VwPScnKSkKICB9KQogIGlmKGluaGVyaXRzKHJ1biwgJ3RyeS1lcnJvcicpKSB7CiAgICBuZXh0CiAgfQogIAogIHJ1biA8LSB0cnkoewogICAgb3JhIDwtIGVucmljaEdPKGdlbmU9aWRzJEVOVFJFWklELCBvbnQ9J0JQJywga2V5VHlwZT0nRU5UUkVaSUQnLE9yZ0RiPW9yZ2FuaXNtKQogICAgCiAgICBwbG90IDwtIGRvdHBsb3Qob3JhLCBzaG93Q2F0ZWdvcnk9MTAsIGZvbnQuc2l6ZT0xMCkgKyBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscz1mdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aD00MCkpCiAgICBqcGVnKHBhc3RlKCcuL09SQV9hc3Ryb3MvR08vTUYvY2x1c3Rlcl8nLGksJ19HT19NRl9PUkEuanBnJyxzZXA9JycpLCBoZWlnaHQ9ODAwLCB3aWR0aD04MDApCiAgICBwcmludChwbG90KQogICAgZGV2Lm9mZigpCiAgICAKICAgIHdyaXRlLmNzdihvcmEsIGZpbGU9cGFzdGUoJy4vT1JBX2FzdHJvcy9HTy9NRi9jbHVzdGVyXycsaSwnX0dPX01GX09SQS5jc3YnLHNlcD0nJykpCiAgfSkKICBpZihpbmhlcml0cyhydW4sICd0cnktZXJyb3InKSkgewogICAgbmV4dAogIH0KICAKICBydW4gPC0gdHJ5KHsKICAgIG9yYSA8LSBlbnJpY2hLRUdHKGdlbmU9aWRzJEVOVFJFWklELCBvcmdhbmlzbT1rb3JnKQogICAgCiAgICBwbG90IDwtIGRvdHBsb3Qob3JhLCBzaG93Q2F0ZWdvcnk9MTAsIGZvbnQuc2l6ZT0xMCkgKyBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscz1mdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aD00MCkpCiAgICBqcGVnKHBhc3RlKCcuL09SQV9hc3Ryb3MvS0VHRy9jbHVzdGVyXycsaSwnX0tFR0dfT1JBLmpwZycsc2VwPScnKSwgaGVpZ2h0PTgwMCwgd2lkdGg9ODAwKQogICAgcHJpbnQocGxvdCkKICAgIGRldi5vZmYoKQogICAgCiAgICB3cml0ZS5jc3Yob3JhLCBmaWxlPXBhc3RlKCcuL09SQV9hc3Ryb3MvS0VHRy9jbHVzdGVyXycsaSwnX0tFR0dfT1JBLmNzdicsc2VwPScnKSkKICB9KQogIGlmKGluaGVyaXRzKHJ1biwgJ3RyeS1lcnJvcicpKSB7CiAgICBuZXh0CiAgfQogIAp9CmBgYAoKIyMjIFJlY2x1c3RlcmluZyBJbmhpYml0b3J5IG5ldXJvbnMKTGlrZXdpc2Ugd2UgZHVnIGludG8gdGhlIGluaGliaXRvcnkgbmV1cm9uIHN1YnNldHMuIAoKYGBge3IgSW5oaWJSZWNsdXN0ZXIsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxNiwgZmlnLndpZHRoID0gMTB9CgpJZGVudHMoY29udHJvbCkgPC0gJ0Fubm90YXRpb25zJwppbmhpYiA8LSBzdWJzZXQoY29udHJvbCwgaWRlbnRzPWMoJ1BWIEluaGliaXRvcnkgTmV1cm9ucycsJ1ZJUCBJbmhpYml0b3J5IE5ldXJvbnMnLCdTU1QgSW5oaWJpdG9yeSBOZXVyb25zJykpCkRlZmF1bHRBc3NheShpbmhpYikgPC0gJ1JOQScKCmluaGliIDwtIERpZXRTZXVyYXQoaW5oaWIsIGFzc2F5cz0nUk5BJykKaW5oaWIgPC0gU0NUcmFuc2Zvcm0oaW5oaWIsIHZhcnMudG8ucmVncmVzcyA9IGMoJ3BlcmNlbnQubWl0bycsJ3BlcmNlbnQucmlibycpLHZlcmJvc2U9RkFMU0UpCmluaGliIDwtIFJ1blBDQShpbmhpYikKaW5oaWIgPC0gUnVuVU1BUChpbmhpYiwgZGltcz0xOjMwKQppbmhpYiA8LSBGaW5kTmVpZ2hib3JzKGluaGliLCByZWR1Y3Rpb249J3BjYScsIGRpbXM9MTozMCkKcmVzb2x1dGlvbnMgPC0gYygwLjIsIDAuMywgMC40LCAwLjUsIDAuNiwgMC43LDAuOCwgMC45LDEpCmluaGliIDwtIEZpbmRDbHVzdGVycyhpbmhpYiwgcmVzb2x1dGlvbj1jKHJlc29sdXRpb25zKSkKCgpwbG90X2dyaWQoRGltUGxvdChpbmhpYiwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuMicsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoaW5oaWIsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjMnLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGluaGliLCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMC40JywgbGFiZWw9VCkgKyBOb0xlZ2VuZCgpLAogICAgICAgICAgRGltUGxvdChpbmhpYiwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuNScsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoaW5oaWIsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjYnLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGluaGliLCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMC43JywgbGFiZWw9VCkgKyBOb0xlZ2VuZCgpLAogICAgICAgICAgRGltUGxvdChpbmhpYiwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuOCcsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoaW5oaWIsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjknLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGluaGliLCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMScsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIG5jb2w9MwogICAgICAgICAgKQpgYGAKCmBgYHtyIEluaGliQ2x1c3RlcnMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxNiwgZmlnLndpZHRoID0gMTB9CkRlZmF1bHRBc3NheShpbmhpYikgPC0gJ1NDVCcKSWRlbnRzKGluaGliKSA8LSAnU0NUX3Nubl9yZXMuMC4yJwppbmhpYltbJ3NldXJhdF9jbHVzdGVycyddXSA8LSBJZGVudHMoaW5oaWIpCgpEaW1QbG90KGluaGliLCBwdC5zaXplPTIsIGxhYmVsPVQsY29scz1jKCJFeGNpdGF0b3J5IE5ldXJvbnMgMSI9IiNmOGI2ZmYiLCAiRXhjaXRhdG9yeSBOZXVyb25zIDIiPSIjZjA5MGZhIiwgIkV4Y2l0YXRvcnkgTmV1cm9ucyAzIj0iI2ZkYTRmZiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA0Ij0iI2QwYTRmZiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA1Ij0iI2ZmNjliNCIsIlBWIEluaGliaXRvcnkgTmV1cm9ucyI9IiM3MmIyZjQiLCJTU1QgSW5oaWJpdG9yeSBOZXVyb25zIj0iIzM0OGZlYiIsIlVuY2xhc3NpZmllZCBOZXVyb25zIDEiPSIjZmZlNTk5IiwiVW5jbGFzc2lmaWVkIE5ldXJvbnMgMiI9IiNmZmQ5NjYiLCJWSVAgSW5oaWJpdG9yeSBOZXVyb25zIj0iIzgyYjZlNSIsIkFzdHJvY3l0ZXMiPSIjMGFkMWM2IiwiT1BDcyI9IiM3M2MxNTIiLCJNaWNyb2dsaWEiPSIjZjVjMTlkIiwiRXhjaXRhdG9yeSBOZXVyb25zIDYiPSIjZWZkNjhiIiwiRXhjaXRhdG9yeSBOZXVyb25zIDciPSIjYjFjMzdjIiwiT2xpZ29kZW5kcm9jeXRlcyI9IiM1OWM1MmEiKSwgZ3JvdXAuYnk9J0Fubm90YXRpb25zJykKYGBgCgpgYGB7ciBJbmhpYk1hcmtlcnMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxNiwgZmlnLndpZHRoID0gMTB9CklkZW50cyhpbmhpYikgPC0gJ3NldXJhdF9jbHVzdGVycycKaW5oaWIgPC0gUHJlcFNDVEZpbmRNYXJrZXJzKGluaGliKQppbmhpYm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoaW5oaWIsIG9ubHkucG9zPVQsIG1pbi5kaWZmLnBjdCA9IDAuNCkKCmRhdGF0YWJsZShpbmhpYm1hcmtlcnMpCmBgYAoKIyMjIyBJbmhpYml0b3J5IHBhdGh3YXlzIGFuYWx5c2lzCgpgYGB7ciBJbmhpYlBhdGh3YXlzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmRpci5jcmVhdGUoJy4vT1JBX2luaGliJykKZGlyLmNyZWF0ZSgnLi9PUkFfaW5oaWIvS0VHRycpCmRpci5jcmVhdGUoJy4vT1JBX2luaGliL0dPJykKZGlyLmNyZWF0ZSgnLi9PUkFfaW5oaWIvR08vQlAnKQpkaXIuY3JlYXRlKCcuL09SQV9pbmhpYi9HTy9DQycpCmRpci5jcmVhdGUoJy4vT1JBX2luaGliL0dPL01GJykKCgpmb3IgKGkgaW4gdW5pcXVlKElkZW50cyhpbmhpYikpKXsKICBtYXJrcyA8LSBGaW5kTWFya2VycyhpbmhpYiwgaWRlbnQuMT1pLCBvbmx5LnBvcz1ULCBtaW4uZGlmZi5wY3QgPSAwLjMsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuNSkKICBtYXJrcyA8LSBzdWJzZXQobWFya3MsIHBfdmFsX2FkaiA8IDAuMDUpCiAgZ2VuZXMgPC0gcm93bmFtZXMobWFya3MpCiAgCiAgaWRzIDwtIGJpdHIoZ2VuZXMsIGZyb21UeXBlPSdTWU1CT0wnLCB0b1R5cGU9J0VOVFJFWklEJywgT3JnRGI9b3JnYW5pc20pCiAgCiAgcnVuIDwtIHRyeSh7CiAgICBvcmEgPC0gZW5yaWNoR08oZ2VuZT1pZHMkRU5UUkVaSUQsIG9udD0nQlAnLCBrZXlUeXBlPSdFTlRSRVpJRCcsT3JnRGI9b3JnYW5pc20pCiAgICAKICAgIHBsb3QgPC0gZG90cGxvdChvcmEsIHNob3dDYXRlZ29yeT0xMCwgZm9udC5zaXplPTEwKSArIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzPWZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoPTQwKSkKICAgIGpwZWcocGFzdGUoJy4vT1JBX2luaGliL0dPL0JQL2NsdXN0ZXJfJyxpLCdfR09fQlBfT1JBLmpwZycsc2VwPScnKSwgaGVpZ2h0PTgwMCwgd2lkdGg9ODAwKQogICAgcHJpbnQocGxvdCkKICAgIGRldi5vZmYoKQogICAgCiAgICB3cml0ZS5jc3Yob3JhLCBmaWxlPXBhc3RlKCcuL09SQV9pbmhpYi9HTy9CUC9jbHVzdGVyXycsaSwnX0dPX0JQX09SQS5jc3YnLHNlcD0nJykpCiAgfSkKICBpZihpbmhlcml0cyhydW4sICd0cnktZXJyb3InKSkgewogICAgbmV4dAogIH0KICBydW4gPC0gdHJ5KHsKICAgIG9yYSA8LSBlbnJpY2hHTyhnZW5lPWlkcyRFTlRSRVpJRCwgb250PSdDQycsIGtleVR5cGU9J0VOVFJFWklEJyxPcmdEYj1vcmdhbmlzbSkKICAgIAogICAgcGxvdCA8LSBkb3RwbG90KG9yYSwgc2hvd0NhdGVnb3J5PTEwLCBmb250LnNpemU9MTApICsgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHM9ZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGg9NDApKQogICAganBlZyhwYXN0ZSgnLi9PUkFfaW5oaWIvR08vQ0MvY2x1c3Rlcl8nLGksJ19HT19DQ19PUkEuanBnJyxzZXA9JycpLCBoZWlnaHQ9ODAwLCB3aWR0aD04MDApCiAgICBwcmludChwbG90KQogICAgZGV2Lm9mZigpCiAgICAKICAgIHdyaXRlLmNzdihvcmEsIGZpbGU9cGFzdGUoJy4vT1JBX2luaGliL0dPL0NDL2NsdXN0ZXJfJyxpLCdfR09fQ0NfT1JBLmNzdicsc2VwPScnKSkKICB9KQogIGlmKGluaGVyaXRzKHJ1biwgJ3RyeS1lcnJvcicpKSB7CiAgICBuZXh0CiAgfQogIAogIHJ1biA8LSB0cnkoewogICAgb3JhIDwtIGVucmljaEdPKGdlbmU9aWRzJEVOVFJFWklELCBvbnQ9J0JQJywga2V5VHlwZT0nRU5UUkVaSUQnLE9yZ0RiPW9yZ2FuaXNtKQogICAgCiAgICBwbG90IDwtIGRvdHBsb3Qob3JhLCBzaG93Q2F0ZWdvcnk9MTAsIGZvbnQuc2l6ZT0xMCkgKyBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscz1mdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aD00MCkpCiAgICBqcGVnKHBhc3RlKCcuL09SQV9pbmhpYi9HTy9NRi9jbHVzdGVyXycsaSwnX0dPX01GX09SQS5qcGcnLHNlcD0nJyksIGhlaWdodD04MDAsIHdpZHRoPTgwMCkKICAgIHByaW50KHBsb3QpCiAgICBkZXYub2ZmKCkKICAgIAogICAgd3JpdGUuY3N2KG9yYSwgZmlsZT1wYXN0ZSgnLi9PUkFfaW5oaWIvR08vTUYvY2x1c3Rlcl8nLGksJ19HT19NRl9PUkEuY3N2JyxzZXA9JycpKQogIH0pCiAgaWYoaW5oZXJpdHMocnVuLCAndHJ5LWVycm9yJykpIHsKICAgIG5leHQKICB9CiAgCiAgcnVuIDwtIHRyeSh7CiAgICBvcmEgPC0gZW5yaWNoS0VHRyhnZW5lPWlkcyRFTlRSRVpJRCwgb3JnYW5pc209a29yZykKICAgIAogICAgcGxvdCA8LSBkb3RwbG90KG9yYSwgc2hvd0NhdGVnb3J5PTEwLCBmb250LnNpemU9MTApICsgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHM9ZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGg9NDApKQogICAganBlZyhwYXN0ZSgnLi9PUkFfaW5oaWIvS0VHRy9jbHVzdGVyXycsaSwnX0tFR0dfT1JBLmpwZycsc2VwPScnKSwgaGVpZ2h0PTgwMCwgd2lkdGg9ODAwKQogICAgcHJpbnQocGxvdCkKICAgIGRldi5vZmYoKQogICAgCiAgICB3cml0ZS5jc3Yob3JhLCBmaWxlPXBhc3RlKCcuL09SQV9pbmhpYi9LRUdHL2NsdXN0ZXJfJyxpLCdfS0VHR19PUkEuY3N2JyxzZXA9JycpKQogIH0pCiAgaWYoaW5oZXJpdHMocnVuLCAndHJ5LWVycm9yJykpIHsKICAgIG5leHQKICB9CiAgCn0KYGBgCgojIyMgUmVjbHVzdGVyaW5nIEV4Y2l0YXRvcnkgbmV1cm9ucwpBbmQgb25jZSBhZ2FpbiBmb3IgZXhjaXRhdG9yeSBuZXVyb25zIQoKYGBge3IgRXhjaXRlUmVjbHVzdGVyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQpJZGVudHMoY29udHJvbCkgPC0gJ0Fubm90YXRpb25zJwpleGNpdCA8LSBzdWJzZXQoY29udHJvbCwgaWRlbnRzPWMoJ0V4Y2l0YXRvcnkgTmV1cm9ucyAxJywnRXhjaXRhdG9yeSBOZXVyb25zIDInLCdFeGNpdGF0b3J5IE5ldXJvbnMgMycsJ0V4Y2l0YXRvcnkgTmV1cm9ucyA0JywnRXhjaXRhdG9yeSBOZXVyb25zIDUnKSkKRGVmYXVsdEFzc2F5KGV4Y2l0KSA8LSAnUk5BJwoKZXhjaXQgPC0gRGlldFNldXJhdChleGNpdCwgYXNzYXlzPSdSTkEnKQpleGNpdCA8LSBTQ1RyYW5zZm9ybShleGNpdCwgdmFycy50by5yZWdyZXNzID0gYygncGVyY2VudC5taXRvJywncGVyY2VudC5yaWJvJyksdmVyYm9zZT1GQUxTRSkKZXhjaXQgPC0gUnVuUENBKGV4Y2l0KQpleGNpdCA8LSBSdW5VTUFQKGV4Y2l0LCBkaW1zPTE6MzApCmV4Y2l0IDwtIEZpbmROZWlnaGJvcnMoZXhjaXQsIHJlZHVjdGlvbj0ncGNhJywgZGltcz0xOjMwKQpyZXNvbHV0aW9ucyA8LSBjKDAuMiwgMC4zLCAwLjQsIDAuNSwgMC42LCAwLjcsMC44LCAwLjksMSkKZXhjaXQgPC0gRmluZENsdXN0ZXJzKGV4Y2l0LCByZXNvbHV0aW9uPWMocmVzb2x1dGlvbnMpKQoKcGxvdF9ncmlkKERpbVBsb3QoZXhjaXQsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjInLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGV4Y2l0LCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMC4zJywgbGFiZWw9VCkgKyBOb0xlZ2VuZCgpLAogICAgICAgICAgRGltUGxvdChleGNpdCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuNCcsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoZXhjaXQsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjUnLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGV4Y2l0LCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMC42JywgbGFiZWw9VCkgKyBOb0xlZ2VuZCgpLAogICAgICAgICAgRGltUGxvdChleGNpdCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjAuNycsIGxhYmVsPVQpICsgTm9MZWdlbmQoKSwKICAgICAgICAgIERpbVBsb3QoZXhjaXQsIGdyb3VwLmJ5PSdTQ1Rfc25uX3Jlcy4wLjgnLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBEaW1QbG90KGV4Y2l0LCBncm91cC5ieT0nU0NUX3Nubl9yZXMuMC45JywgbGFiZWw9VCkgKyBOb0xlZ2VuZCgpLAogICAgICAgICAgRGltUGxvdChleGNpdCwgZ3JvdXAuYnk9J1NDVF9zbm5fcmVzLjEnLCBsYWJlbD1UKSArIE5vTGVnZW5kKCksCiAgICAgICAgICBuY29sPTMKICAgICAgICAgICkKYGBgCgpgYGB7ciBFeGNpdGVDbHVzdGVyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQpEZWZhdWx0QXNzYXkoZXhjaXQpIDwtICdTQ1QnCklkZW50cyhleGNpdCkgPC0gJ1NDVF9zbm5fcmVzLjAuMicKZXhjaXRbWydzZXVyYXRfY2x1c3RlcnMnXV0gPC0gSWRlbnRzKGV4Y2l0KQoKRGltUGxvdChleGNpdCwgcHQuc2l6ZT0yLCBsYWJlbD1ULGNvbHM9YygiRXhjaXRhdG9yeSBOZXVyb25zIDEiPSIjZjhiNmZmIiwgIkV4Y2l0YXRvcnkgTmV1cm9ucyAyIj0iI2YwOTBmYSIsICJFeGNpdGF0b3J5IE5ldXJvbnMgMyI9IiNmZGE0ZmYiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNCI9IiNkMGE0ZmYiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNSI9IiNmZjY5YjQiLCJQViBJbmhpYml0b3J5IE5ldXJvbnMiPSIjNzJiMmY0IiwiU1NUIEluaGliaXRvcnkgTmV1cm9ucyI9IiMzNDhmZWIiLCJVbmNsYXNzaWZpZWQgTmV1cm9ucyAxIj0iI2ZmZTU5OSIsIlVuY2xhc3NpZmllZCBOZXVyb25zIDIiPSIjZmZkOTY2IiwiVklQIEluaGliaXRvcnkgTmV1cm9ucyI9IiM4MmI2ZTUiLCJBc3Ryb2N5dGVzIj0iIzBhZDFjNiIsIk9QQ3MiPSIjNzNjMTUyIiwiTWljcm9nbGlhIj0iI2Y1YzE5ZCIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA2Ij0iI2VmZDY4YiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA3Ij0iI2IxYzM3YyIsIk9saWdvZGVuZHJvY3l0ZXMiPSIjNTljNTJhIiksIGdyb3VwLmJ5PSdBbm5vdGF0aW9ucycpCmBgYAoKYGBge3IgRXhjaXRlTWFya2Vycywgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLmFsaWduPSJjZW50ZXIiLCByZXN1bHRzPSdoaWRlJywgZmlnLmhlaWdodCA9IDE2LCBmaWcud2lkdGggPSAxMH0KSWRlbnRzKGV4Y2l0KSA8LSAnc2V1cmF0X2NsdXN0ZXJzJwpleGNpdCA8LSBQcmVwU0NURmluZE1hcmtlcnMoZXhjaXQpCmV4Y2l0bWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhleGNpdCwgb25seS5wb3M9VCwgbWluLmRpZmYucGN0ID0gMC40KQpkYXRhdGFibGUoZXhjaXRtYXJrZXJzKQpgYGAKCiMjIyMgRXhjaXRhdG9yeSBwYXRod2F5IGFuYWx5c2lzCmBgYHtyIEV4Y2l0ZVBhdGh3YXlzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9J2hpZGUnLCBmaWcuaGVpZ2h0ID0gMTYsIGZpZy53aWR0aCA9IDEwfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmRpci5jcmVhdGUoJy4vT1JBX2V4Y2l0JykKZGlyLmNyZWF0ZSgnLi9PUkFfZXhjaXQvS0VHRycpCmRpci5jcmVhdGUoJy4vT1JBX2V4Y2l0L0dPJykKZGlyLmNyZWF0ZSgnLi9PUkFfZXhjaXQvR08vQlAnKQpkaXIuY3JlYXRlKCcuL09SQV9leGNpdC9HTy9DQycpCmRpci5jcmVhdGUoJy4vT1JBX2V4Y2l0L0dPL01GJykKCgpmb3IgKGkgaW4gdW5pcXVlKElkZW50cyhleGNpdCkpKXsKICBtYXJrcyA8LSBGaW5kTWFya2VycyhleGNpdCwgaWRlbnQuMT1pLCBvbmx5LnBvcz1ULCBtaW4uZGlmZi5wY3QgPSAwLjMsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuNSkKICBtYXJrcyA8LSBzdWJzZXQobWFya3MsIHBfdmFsX2FkaiA8IDAuMDUpCiAgZ2VuZXMgPC0gcm93bmFtZXMobWFya3MpCiAgCiAgaWRzIDwtIGJpdHIoZ2VuZXMsIGZyb21UeXBlPSdTWU1CT0wnLCB0b1R5cGU9J0VOVFJFWklEJywgT3JnRGI9b3JnYW5pc20pCiAgCiAgcnVuIDwtIHRyeSh7CiAgICBvcmEgPC0gZW5yaWNoR08oZ2VuZT1pZHMkRU5UUkVaSUQsIG9udD0nQlAnLCBrZXlUeXBlPSdFTlRSRVpJRCcsT3JnRGI9b3JnYW5pc20pCiAgICAKICAgIHBsb3QgPC0gZG90cGxvdChvcmEsIHNob3dDYXRlZ29yeT0xMCwgZm9udC5zaXplPTEwKSArIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzPWZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoPTQwKSkKICAgIGpwZWcocGFzdGUoJy4vT1JBX2V4Y2l0L0dPL0JQL2NsdXN0ZXJfJyxpLCdfR09fQlBfT1JBLmpwZycsc2VwPScnKSwgaGVpZ2h0PTgwMCwgd2lkdGg9ODAwKQogICAgcHJpbnQocGxvdCkKICAgIGRldi5vZmYoKQogICAgCiAgICB3cml0ZS5jc3Yob3JhLCBmaWxlPXBhc3RlKCcuL09SQV9leGNpdC9HTy9CUC9jbHVzdGVyXycsaSwnX0dPX0JQX09SQS5jc3YnLHNlcD0nJykpCiAgfSkKICBpZihpbmhlcml0cyhydW4sICd0cnktZXJyb3InKSkgewogICAgbmV4dAogIH0KICBydW4gPC0gdHJ5KHsKICAgIG9yYSA8LSBlbnJpY2hHTyhnZW5lPWlkcyRFTlRSRVpJRCwgb250PSdDQycsIGtleVR5cGU9J0VOVFJFWklEJyxPcmdEYj1vcmdhbmlzbSkKICAgIAogICAgcGxvdCA8LSBkb3RwbG90KG9yYSwgc2hvd0NhdGVnb3J5PTEwLCBmb250LnNpemU9MTApICsgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHM9ZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGg9NDApKQogICAganBlZyhwYXN0ZSgnLi9PUkFfZXhjaXQvR08vQ0MvY2x1c3Rlcl8nLGksJ19HT19DQ19PUkEuanBnJyxzZXA9JycpLCBoZWlnaHQ9ODAwLCB3aWR0aD04MDApCiAgICBwcmludChwbG90KQogICAgZGV2Lm9mZigpCiAgICAKICAgIHdyaXRlLmNzdihvcmEsIGZpbGU9cGFzdGUoJy4vT1JBX2V4Y2l0L0dPL0NDL2NsdXN0ZXJfJyxpLCdfR09fQ0NfT1JBLmNzdicsc2VwPScnKSkKICB9KQogIGlmKGluaGVyaXRzKHJ1biwgJ3RyeS1lcnJvcicpKSB7CiAgICBuZXh0CiAgfQogIAogIHJ1biA8LSB0cnkoewogICAgb3JhIDwtIGVucmljaEdPKGdlbmU9aWRzJEVOVFJFWklELCBvbnQ9J0JQJywga2V5VHlwZT0nRU5UUkVaSUQnLE9yZ0RiPW9yZ2FuaXNtKQogICAgCiAgICBwbG90IDwtIGRvdHBsb3Qob3JhLCBzaG93Q2F0ZWdvcnk9MTAsIGZvbnQuc2l6ZT0xMCkgKyBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscz1mdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aD00MCkpCiAgICBqcGVnKHBhc3RlKCcuL09SQV9leGNpdC9HTy9NRi9jbHVzdGVyXycsaSwnX0dPX01GX09SQS5qcGcnLHNlcD0nJyksIGhlaWdodD04MDAsIHdpZHRoPTgwMCkKICAgIHByaW50KHBsb3QpCiAgICBkZXYub2ZmKCkKICAgIAogICAgd3JpdGUuY3N2KG9yYSwgZmlsZT1wYXN0ZSgnLi9PUkFfZXhjaXQvR08vTUYvY2x1c3Rlcl8nLGksJ19HT19NRl9PUkEuY3N2JyxzZXA9JycpKQogIH0pCiAgaWYoaW5oZXJpdHMocnVuLCAndHJ5LWVycm9yJykpIHsKICAgIG5leHQKICB9CiAgCiAgcnVuIDwtIHRyeSh7CiAgICBvcmEgPC0gZW5yaWNoS0VHRyhnZW5lPWlkcyRFTlRSRVpJRCwgb3JnYW5pc209a29yZykKICAgIAogICAgcGxvdCA8LSBkb3RwbG90KG9yYSwgc2hvd0NhdGVnb3J5PTEwLCBmb250LnNpemU9MTApICsgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHM9ZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGg9NDApKQogICAganBlZyhwYXN0ZSgnLi9PUkFfZXhjaXQvS0VHRy9jbHVzdGVyXycsaSwnX0tFR0dfT1JBLmpwZycsc2VwPScnKSwgaGVpZ2h0PTgwMCwgd2lkdGg9ODAwKQogICAgcHJpbnQocGxvdCkKICAgIGRldi5vZmYoKQogICAgCiAgICB3cml0ZS5jc3Yob3JhLCBmaWxlPXBhc3RlKCcuL09SQV9leGNpdC9LRUdHL2NsdXN0ZXJfJyxpLCdfS0VHR19PUkEuY3N2JyxzZXA9JycpKQogIH0pCiAgaWYoaW5oZXJpdHMocnVuLCAndHJ5LWVycm9yJykpIHsKICAgIG5leHQKICB9CiAgCn0KCmBgYAoKIyMjIyMgUHVibGljYXRpb24gcGxvdHMKYGBge3IgUHViUGxvdHMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQgPSAxNiwgZmlnLndpZHRoID0gMTB9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRmlndXJlIDEKIyMgMUE6IFVNQVAgcGxvdAojIyAxQjogQ29tcG9zaXRpb24gcGxvdAoKdGVtcCA8LSBjb250cm9sCnRlbXAgPC0gc3Vic2V0KHRlbXAsIGlkZW50cz1jKCdFeGNpdGF0b3J5IE5ldXJvbnMgMScsICdFeGNpdGF0b3J5IE5ldXJvbnMgMicsJ0V4Y2l0YXRvcnkgTmV1cm9ucyAzJywnRXhjaXRhdG9yeSBOZXVyb25zIDQnLCdFeGNpdGF0b3J5IE5ldXJvbnMgNScsJ0V4Y2l0YXRvcnkgTmV1cm9ucyA2JywnU1NUIEluaGliaXRvcnkgTmV1cm9ucycsJ1BWIEluaGliaXRvcnkgTmV1cm9ucycsJ1ZJUCBJbmhpYml0b3J5IE5ldXJvbnMnLCdNaWNyb2dsaWEnLCdPUENzJywnQXN0cm9jeXRlcycsJ09saWdvZGVuZHJvY3l0ZXMnLCdVbmNsYXNzaWZpZWQgTmV1cm9ucyAxJywnVW5jbGFzc2lmaWVkIE5ldXJvbnMgMicpKQoKdGVtcEBtZXRhLmRhdGEkQW5ub3RhdGlvbnMgPC1zdHJpbmdyOjpzdHJfd3JhcCh0ZW1wQG1ldGEuZGF0YSRBbm5vdGF0aW9ucywgMTUpCnAxYSA8LSBEaW1QbG90KHRlbXAsIGdyb3VwLmJ5PSdBbm5vdGF0aW9ucycsIGxhYmVsPVQsIHB0LnNpemU9MS43NSwgbGFiZWwuc2l6ZT00LCBjb2xzPWMoIkV4Y2l0YXRvcnlcbk5ldXJvbnMgMSI9IiNmOGI2ZmYiLCAiRXhjaXRhdG9yeVxuTmV1cm9ucyAyIj0iI2YwOTBmYSIsICJFeGNpdGF0b3J5XG5OZXVyb25zIDMiPSIjZmRhNGZmIiwiRXhjaXRhdG9yeVxuTmV1cm9ucyA0Ij0iI2QwYTRmZiIsIkV4Y2l0YXRvcnlcbk5ldXJvbnMgNSI9IiNmZjY5YjQiLCJQViBJbmhpYml0b3J5XG5OZXVyb25zIj0iIzcyYjJmNCIsIlNTVCBJbmhpYml0b3J5XG5OZXVyb25zIj0iIzM0OGZlYiIsIlVuY2xhc3NpZmllZFxuTmV1cm9ucyAxIj0iI2ZmZTU5OSIsIlVuY2xhc3NpZmllZFxuTmV1cm9ucyAyIj0iI2ZmZDk2NiIsIlZJUCBJbmhpYml0b3J5XG5OZXVyb25zIj0iIzFjNTFlNiIsIkFzdHJvY3l0ZXMiPSIjMGFkMWM2IiwiT1BDcyI9IiM3M2MxNTIiLCJNaWNyb2dsaWEiPSIjZjVjMTlkIiwiRXhjaXRhdG9yeVxuTmV1cm9ucyA2Ij0iI2VmZDY4YiIsIkV4Y2l0YXRvcnlcbk5ldXJvbnMgNyI9IiNiMWMzN2MiLCJPbGlnb2RlbmRyb2N5dGVzIj0iIzU5YzUyYSIpLCByZXBlbD1UKSArIE5vTGVnZW5kKCkgKyBnZ3RpdGxlKE5VTEwpCgpjb3VudHMgPC0gdGFibGUoSWRlbnRzKHRlbXApLCB0ZW1wJHNhbXApCmNvdW50cyA8LSBwcm9wLnRhYmxlKGNvdW50cywgbWFyZ2luPTIpKjEwMAojT3JnYW5pemUgdGhlIGRhdGFmcmFtZSBmb3IgZWFzeSBwbG90dGluZwpkZiA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChjb3VudHMpIApkZiRjbHVzdGVyIDwtIHJvd25hbWVzKGRmKQpkZiA8LSBkZiAlPiUgZ2F0aGVyKC4sIHRyZWF0bWVudCwgcHJvcHMsIENvbnRyb2w6Q29udHJvbCwgZmFjdG9yX2tleT1UKSAKZ3JvdXAuY29sb3JzIDwtIGMoIkV4Y2l0YXRvcnkgTmV1cm9ucyAxIj0iI2Y4YjZmZiIsICJFeGNpdGF0b3J5IE5ldXJvbnMgMiI9IiNmMDkwZmEiLCAiRXhjaXRhdG9yeSBOZXVyb25zIDMiPSIjZmRhNGZmIiwiRXhjaXRhdG9yeSBOZXVyb25zIDQiPSIjZDBhNGZmIiwiRXhjaXRhdG9yeSBOZXVyb25zIDUiPSIjZmY2OWI0IiwiUFYgSW5oaWJpdG9yeSBOZXVyb25zIj0iIzcyYjJmNCIsIlNTVCBJbmhpYml0b3J5IE5ldXJvbnMiPSIjMzQ4ZmViIiwiVW5jbGFzc2lmaWVkIE5ldXJvbnMgMSI9IiNmZmU1OTkiLCJVbmNsYXNzaWZpZWQgTmV1cm9ucyAyIj0iI2ZmZDk2NiIsIlZJUCBJbmhpYml0b3J5IE5ldXJvbnMiPSIjMWM1MWU2IiwiQXN0cm9jeXRlcyI9IiMwYWQxYzYiLCJPUENzIj0iIzczYzE1MiIsIk1pY3JvZ2xpYSI9IiNmNWMxOWQiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNiI9IiNlZmQ2OGIiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNyI9IiNiMWMzN2MiLCJPbGlnb2RlbmRyb2N5dGVzIj0iIzU5YzUyYSIpCgpwMWIgPC0gZ2dwbG90KGRmLCBhZXMoeD10cmVhdG1lbnQsIHk9cHJvcHMsIGZpbGw9Y2x1c3RlcikpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCBjb2xvcj0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArIGxhYnModGl0bGU9JycseD0nJyx5PSdQZXJjZW50YWdlIG9mIGNlbGxzJywgZmlsbD0nQ2x1c3RlcjonKSArIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MjApLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDMxNSwgc2l6ZT0wKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Z3JvdXAuY29sb3JzKSArIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCnBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSkKCnRpdGxlIDwtIGdnZHJhdygpICsgCiAgZHJhd19sYWJlbCgKICAgICJGaWd1cmUgMSIsCiAgICBmb250ZmFjZSA9ICdib2xkJywKICAgIHggPSAwLAogICAgaGp1c3QgPSAwLAogICAgc2l6ZT0yMAogICkgKwogIHRoZW1lKAogICAgIyBhZGQgbWFyZ2luIG9uIHRoZSBsZWZ0IG9mIHRoZSBkcmF3aW5nIGNhbnZhcywKICAgICMgc28gdGl0bGUgaXMgYWxpZ25lZCB3aXRoIGxlZnQgZWRnZSBvZiBmaXJzdCBwbG90CiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCAwLCAwLCA3KQogICkKCnBsb3RzIDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZChwMWEsIHAxYiwgbmNvbD0yLCByZWxfd2lkdGhzPWMoMC42LDAuNCksIGxhYmVscz1jKCdBJywnQicpKQpmMSA8LSBjb3dwbG90OjpwbG90X2dyaWQodGl0bGUsIHBsb3RzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoMC4xLDEpKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBGaWd1cmUgMgojIyAyQTogVU1BUCBwbG90IGZvciBJbmhpYnMKIyMgMkI6IFVNQVAgcGxvdCBmb3IgRXhjaXQKIyMgMkM6IFZsbiBmb3IgSW5oaWIgbWFya2VycwojIyAyRDogVmxuIGZvciBFeGNpdCBtYXJrZXJzCiMjIDJFOiBDb21wb3NpdGlvbiBmb3IgSW5oaWJzCiMjIDJGOiBDb21wb3NpdGlvbiBmb3IgRXhjaXQKCnRlbXBpbmhpYiA8LSBpbmhpYgp0ZW1waW5oaWJAbWV0YS5kYXRhJEFubm90YXRpb25zIDwtc3RyaW5ncjo6c3RyX3dyYXAodGVtcGluaGliQG1ldGEuZGF0YSRBbm5vdGF0aW9ucywgMTUpCnAyYSA8LSBEaW1QbG90KHRlbXBpbmhpYiwgcHQuc2l6ZT0yLCBsYWJlbD1ULHJlcGVsPVQsbGFiZWwuc2l6ZT00LGNvbHM9YygiRXhjaXRhdG9yeVxuTmV1cm9ucyAxIj0iI2Y4YjZmZiIsICJFeGNpdGF0b3J5XG5OZXVyb25zIDIiPSIjZjA5MGZhIiwgIkV4Y2l0YXRvcnlcbk5ldXJvbnMgMyI9IiNmZGE0ZmYiLCJFeGNpdGF0b3J5XG5OZXVyb25zIDQiPSIjZDBhNGZmIiwiRXhjaXRhdG9yeVxuTmV1cm9ucyA1Ij0iI2ZmNjliNCIsIlBWIEluaGliaXRvcnlcbk5ldXJvbnMiPSIjNzJiMmY0IiwiU1NUIEluaGliaXRvcnlcbk5ldXJvbnMiPSIjMzQ4ZmViIiwiVW5jbGFzc2lmaWVkXG5OZXVyb25zIDEiPSIjZmZlNTk5IiwiVW5jbGFzc2lmaWVkXG5OZXVyb25zIDIiPSIjZmZkOTY2IiwiVklQIEluaGliaXRvcnlcbk5ldXJvbnMiPSIjMWM1MWU2IiwiQXN0cm9jeXRlcyI9IiMwYWQxYzYiLCJPUENzIj0iIzczYzE1MiIsIk1pY3JvZ2xpYSI9IiNmNWMxOWQiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNiI9IiNlZmQ2OGIiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNyI9IiNiMWMzN2MiLCJPbGlnb2RlbmRyb2N5dGVzIj0iIzU5YzUyYSIpLCBncm91cC5ieT0nQW5ub3RhdGlvbnMnKSArIE5vTGVnZW5kKCkrIGdndGl0bGUoTlVMTCkKCmluaGliX2dlbmVzIDwtIGMoJ1NveDUnLCdHcmluM2EnLCdWaXAnLCdFcmJiNCcsJ0FkYXJiMicsJ0dhZDEnKQpwMmMgPC0gVmxuUGxvdChpbmhpYiwgZmVhdHVyZXM9YyhpbmhpYl9nZW5lcyksIHN0YWNrPVRSVUUsIHNvcnQ9RkFMU0UsIGZsaXA9VFJVRSxncm91cC5ieT0nQW5ub3RhdGlvbnMnLGNvbHM9YygiRXhjaXRhdG9yeSBOZXVyb25zIDEiPSIjZjhiNmZmIiwgIkV4Y2l0YXRvcnkgTmV1cm9ucyAyIj0iI2YwOTBmYSIsICJFeGNpdGF0b3J5IE5ldXJvbnMgMyI9IiNmZGE0ZmYiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNCI9IiNkMGE0ZmYiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNSI9IiNmZjY5YjQiLCJQViBJbmhpYml0b3J5IE5ldXJvbnMiPSIjNzJiMmY0IiwiU1NUIEluaGliaXRvcnkgTmV1cm9ucyI9IiMzNDhmZWIiLCJVbmNsYXNzaWZpZWQgTmV1cm9ucyAxIj0iI2ZmZTU5OSIsIlVuY2xhc3NpZmllZCBOZXVyb25zIDIiPSIjZmZkOTY2IiwiVklQIEluaGliaXRvcnkgTmV1cm9ucyI9IiMxYzUxZTYiLCJBc3Ryb2N5dGVzIj0iIzBhZDFjNiIsIk9QQ3MiPSIjNzNjMTUyIiwiTWljcm9nbGlhIj0iI2Y1YzE5ZCIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA2Ij0iI2VmZDY4YiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA3Ij0iI2IxYzM3YyIsIk9saWdvZGVuZHJvY3l0ZXMiPSIjNTljNTJhIiksIGZpbGwuYnk9J2lkZW50JykgKyB0aGVtZShsZWdlbmQucG9zaXRpb249J25vbmUnKSsgbGFicyh0aXRsZT0nJyx4PScnKSArIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTUpLGF4aXMudGV4dC54ID1lbGVtZW50X3RleHQoc2l6ZT05KSxheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpK3NjYWxlX3hfZGlzY3JldGUobGFiZWxzPWZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKQoKdGVtcGV4Y2l0IDwtIGV4Y2l0CnRlbXBleGNpdEBtZXRhLmRhdGEkQW5ub3RhdGlvbnMgPC1zdHJpbmdyOjpzdHJfd3JhcCh0ZW1wZXhjaXRAbWV0YS5kYXRhJEFubm90YXRpb25zLCAxNSkKcDJiIDwtIERpbVBsb3QodGVtcGV4Y2l0LCBwdC5zaXplPTIsIGxhYmVsPVQscmVwZWw9VCxsYWJlbC5zaXplPTQsY29scz1jKCJFeGNpdGF0b3J5XG5OZXVyb25zIDEiPSIjZjhiNmZmIiwgIkV4Y2l0YXRvcnlcbk5ldXJvbnMgMiI9IiNmMDkwZmEiLCAiRXhjaXRhdG9yeVxuTmV1cm9ucyAzIj0iI2ZkYTRmZiIsIkV4Y2l0YXRvcnlcbk5ldXJvbnMgNCI9IiNkMGE0ZmYiLCJFeGNpdGF0b3J5XG5OZXVyb25zIDUiPSIjZmY2OWI0IiwiUFYgSW5oaWJpdG9yeVxuTmV1cm9ucyI9IiM3MmIyZjQiLCJTU1QgSW5oaWJpdG9yeVxuTmV1cm9ucyI9IiMzNDhmZWIiLCJVbmNsYXNzaWZpZWRcbk5ldXJvbnMgMSI9IiNmZmU1OTkiLCJVbmNsYXNzaWZpZWRcbk5ldXJvbnMgMiI9IiNmZmQ5NjYiLCJWSVAgSW5oaWJpdG9yeVxuTmV1cm9ucyI9IiMxYzUxZTYiLCJBc3Ryb2N5dGVzIj0iIzBhZDFjNiIsIk9QQ3MiPSIjNzNjMTUyIiwiTWljcm9nbGlhIj0iI2Y1YzE5ZCIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA2Ij0iI2VmZDY4YiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA3Ij0iI2IxYzM3YyIsIk9saWdvZGVuZHJvY3l0ZXMiPSIjNTljNTJhIiksIGdyb3VwLmJ5PSdBbm5vdGF0aW9ucycpICsgTm9MZWdlbmQoKSsgZ2d0aXRsZShOVUxMKQoKZXhjaXRfZ2VuZXMgPC0gYygnUm9yYicsJ0ZveHAyJywnQ3V4MScsJ0dhcm5sMycsJ0Nocm0zJywnR3JtOCcpCm9yZGVyZWRfbGV2ZWwgPC0gYygnRXhjaXRhdG9yeSBOZXVyb25zIDEnLCdFeGNpdGF0b3J5IE5ldXJvbnMgMicsJ0V4Y2l0YXRvcnkgTmV1cm9ucyAzJywnRXhjaXRhdG9yeSBOZXVyb25zIDQnLCdFeGNpdGF0b3J5IE5ldXJvbnMgNScpCmV4Y2l0QG1ldGEuZGF0YSRBbm5vdGF0aW9ucyA8LSBmYWN0b3IoeD1leGNpdEBtZXRhLmRhdGEkQW5ub3RhdGlvbnMsbGV2ZWxzPW9yZGVyZWRfbGV2ZWwpCnAyZCA8LSBWbG5QbG90KGV4Y2l0LCBmZWF0dXJlcz1jKGV4Y2l0X2dlbmVzKSwgc3RhY2s9VFJVRSwgc29ydD1GQUxTRSwgZmxpcD1UUlVFLGdyb3VwLmJ5PSdBbm5vdGF0aW9ucycsY29scz1jKCJFeGNpdGF0b3J5IE5ldXJvbnMgMSI9IiNmOGI2ZmYiLCAiRXhjaXRhdG9yeSBOZXVyb25zIDIiPSIjZjA5MGZhIiwgIkV4Y2l0YXRvcnkgTmV1cm9ucyAzIj0iI2ZkYTRmZiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA0Ij0iI2QwYTRmZiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA1Ij0iI2ZmNjliNCIsIlBWIEluaGliaXRvcnlcbk5ldXJvbnMiPSIjNzJiMmY0IiwiU1NUIEluaGliaXRvcnlcbk5ldXJvbnMiPSIjMzQ4ZmViIiwiVW5jbGFzc2lmaWVkIE5ldXJvbnMgMSI9IiNmZmU1OTkiLCJVbmNsYXNzaWZpZWQgTmV1cm9ucyAyIj0iI2ZmZDk2NiIsIlZJUCBJbmhpYml0b3J5XG5OZXVyb25zIj0iIzFjNTFlNiIsIkFzdHJvY3l0ZXMiPSIjMGFkMWM2IiwiT1BDcyI9IiM3M2MxNTIiLCJNaWNyb2dsaWEiPSIjZjVjMTlkIiwiRXhjaXRhdG9yeSBOZXVyb25zIDYiPSIjZWZkNjhiIiwiRXhjaXRhdG9yeSBOZXVyb25zIDciPSIjYjFjMzdjIiwiT2xpZ29kZW5kcm9jeXRlcyI9IiM1OWM1MmEiKSwgZmlsbC5ieT0naWRlbnQnKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsgbGFicyh0aXRsZT0nJyx4PScnKSArIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTUpLGF4aXMudGV4dC54ID1lbGVtZW50X3RleHQoc2l6ZT05KSxheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpK3NjYWxlX3hfZGlzY3JldGUobGFiZWxzPWZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKQoKSWRlbnRzKGluaGliKSA8LSAnQW5ub3RhdGlvbnMnCmNvdW50cyA8LSB0YWJsZShJZGVudHMoaW5oaWIpLCBpbmhpYiRzYW1wKQpjb3VudHMgPC0gcHJvcC50YWJsZShjb3VudHMsIG1hcmdpbj0yKSoxMDAKI09yZ2FuaXplIHRoZSBkYXRhZnJhbWUgZm9yIGVhc3kgcGxvdHRpbmcKZGYgPC0gYXMuZGF0YS5mcmFtZS5tYXRyaXgoY291bnRzKSAKZGYkY2x1c3RlciA8LSByb3duYW1lcyhkZikKZGYgPC0gZGYgJT4lIGdhdGhlciguLCB0cmVhdG1lbnQsIHByb3BzLCBDb250cm9sOkNvbnRyb2wsIGZhY3Rvcl9rZXk9VCkgCmdyb3VwLmNvbG9ycyA8LSBjKCJFeGNpdGF0b3J5XG5OZXVyb25zIDEiPSIjZjhiNmZmIiwgIkV4Y2l0YXRvcnlcbk5ldXJvbnMgMiI9IiNmMDkwZmEiLCAiRXhjaXRhdG9yeVxuTmV1cm9ucyAzIj0iI2ZkYTRmZiIsIkV4Y2l0YXRvcnlcbk5ldXJvbnMgNCI9IiNkMGE0ZmYiLCJFeGNpdGF0b3J5IE5ldXJvbnNcbjUiPSIjZmY2OWI0IiwiUFZcbkluaGliaXRvcnlcbk5ldXJvbnMiPSIjNzJiMmY0IiwiU1NUXG5JbmhpYml0b3J5XG5OZXVyb25zIj0iIzM0OGZlYiIsIlVuY2xhc3NpZmllZCBOZXVyb25zIDEiPSIjZmZlNTk5IiwiVW5jbGFzc2lmaWVkIE5ldXJvbnMgMiI9IiNmZmQ5NjYiLCJWSVBcbkluaGliaXRvcnlcbk5ldXJvbnMiPSIjMWM1MWU2IiwiQXN0cm9jeXRlcyI9IiMwYWQxYzYiLCJPUENzIj0iIzczYzE1MiIsIk1pY3JvZ2xpYSI9IiNmNWMxOWQiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNiI9IiNlZmQ2OGIiLCJFeGNpdGF0b3J5IE5ldXJvbnMgNyI9IiNiMWMzN2MiLCJPbGlnb2RlbmRyb2N5dGVzIj0iIzU5YzUyYSIpCmRmJGNsdXN0ZXIgPC0gc3RyaW5ncjo6c3RyX3dyYXAoZGYkY2x1c3RlciwgOCkKcDJlIDwtIGdncGxvdChkZiwgYWVzKHg9dHJlYXRtZW50LCB5PXByb3BzLCBmaWxsPWNsdXN0ZXIsIGxhYmVsPWNsdXN0ZXIpKSArIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgY29sb3I9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHRpdGxlPScnLHg9JycseT0nUGVyY2VudGFnZSBvZiBjZWxscycsIGZpbGw9J0NsdXN0ZXI6JykgKyB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE1KSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMTUsIHNpemU9MCkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWdyb3VwLmNvbG9ycyxsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLApwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIikpKyBOb0xlZ2VuZCgpKwogIGdlb21fdGV4dChzaXplID0gNCwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpCgpJZGVudHMoZXhjaXQpIDwtICdBbm5vdGF0aW9ucycKY291bnRzIDwtIHRhYmxlKElkZW50cyhleGNpdCksIGV4Y2l0JHNhbXApCmNvdW50cyA8LSBwcm9wLnRhYmxlKGNvdW50cywgbWFyZ2luPTIpKjEwMAojT3JnYW5pemUgdGhlIGRhdGFmcmFtZSBmb3IgZWFzeSBwbG90dGluZwpkZiA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChjb3VudHMpIApkZiRjbHVzdGVyIDwtIHJvd25hbWVzKGRmKQpkZiA8LSBkZiAlPiUgZ2F0aGVyKC4sIHRyZWF0bWVudCwgcHJvcHMsIENvbnRyb2w6Q29udHJvbCwgZmFjdG9yX2tleT1UKSAKZ3JvdXAuY29sb3JzIDwtIGMoIkV4Y2l0YXRvcnlcbk5ldXJvbnMgMSI9IiNmOGI2ZmYiLCAiRXhjaXRhdG9yeVxuTmV1cm9ucyAyIj0iI2YwOTBmYSIsICJFeGNpdGF0b3J5XG5OZXVyb25zIDMiPSIjZmRhNGZmIiwiRXhjaXRhdG9yeVxuTmV1cm9ucyA0Ij0iI2QwYTRmZiIsIkV4Y2l0YXRvcnlcbk5ldXJvbnMgNSI9IiNmZjY5YjQiLCJQViBJbmhpYml0b3J5IE5ldXJvbnMiPSIjNzJiMmY0IiwiU1NUIEluaGliaXRvcnkgTmV1cm9ucyI9IiMzNDhmZWIiLCJVbmNsYXNzaWZpZWQgTmV1cm9ucyAxIj0iI2ZmZTU5OSIsIlVuY2xhc3NpZmllZCBOZXVyb25zIDIiPSIjZmZkOTY2IiwiVklQIEluaGliaXRvcnkgTmV1cm9ucyI9IiMxYzUxZTYiLCJBc3Ryb2N5dGVzIj0iIzBhZDFjNiIsIk9QQ3MiPSIjNzNjMTUyIiwiTWljcm9nbGlhIj0iI2Y1YzE5ZCIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA2Ij0iI2VmZDY4YiIsIkV4Y2l0YXRvcnkgTmV1cm9ucyA3Ij0iI2IxYzM3YyIsIk9saWdvZGVuZHJvY3l0ZXMiPSIjNTljNTJhIikKZGYkY2x1c3RlciA8LSBzdHJpbmdyOjpzdHJfd3JhcChkZiRjbHVzdGVyLCAxNSkKcDJmIDwtIGdncGxvdChkZiwgYWVzKHg9dHJlYXRtZW50LCB5PXByb3BzLCBmaWxsPWNsdXN0ZXIsIGxhYmVsPWNsdXN0ZXIpKSArIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgY29sb3I9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHRpdGxlPScnLHg9JycseT0nUGVyY2VudGFnZSBvZiBjZWxscycsIGZpbGw9J0NsdXN0ZXI6JykgKyB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE1KSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMTUsIHNpemU9MCkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWdyb3VwLmNvbG9ycyxsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLApwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIikpKyBOb0xlZ2VuZCgpKwogIGdlb21fdGV4dChzaXplID0gNCwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpCgoKdGl0bGUgPC0gZ2dkcmF3KCkgKyAKICBkcmF3X2xhYmVsKAogICAgIkZpZ3VyZSAyIiwKICAgIGZvbnRmYWNlID0gJ2JvbGQnLAogICAgeCA9IDAsCiAgICBoanVzdCA9IDAsCiAgICBzaXplPTIwCiAgKSArCiAgdGhlbWUoCiAgICAjIGFkZCBtYXJnaW4gb24gdGhlIGxlZnQgb2YgdGhlIGRyYXdpbmcgY2FudmFzLAogICAgIyBzbyB0aXRsZSBpcyBhbGlnbmVkIHdpdGggbGVmdCBlZGdlIG9mIGZpcnN0IHBsb3QKICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDcpCiAgKQoKdG9wcGxvdHMgPC0gY293cGxvdDo6cGxvdF9ncmlkKHAyYSwgcDJiLCBuY29sPTIsIHJlbF93aWR0aHM9YygwLjUsMC41KSwgbGFiZWxzPWMoJ0EnLCdCJykpCgpsZWZ0cGxvdHMgPC0gY293cGxvdDo6cGxvdF9ncmlkKAogIGNvd3Bsb3Q6OnBsb3RfZ3JpZChOVUxMLHAyYyxOVUxMLG5jb2w9MywgcmVsX3dpZHRocyA9IGMoMC4xLCAwLjgsIDAuMSkpLAogIGNvd3Bsb3Q6OnBsb3RfZ3JpZChOVUxMLHAyZSxOVUxMLG5jb2w9MywgcmVsX3dpZHRocyA9IGMoMC4xLCAwLjgsIDAuMSkpLAogIG5jb2w9MiwgbGFiZWxzPWMoJ0MnKSwgCiAgcmVsX3dpZHRocz1jKDAuNiwwLjQpCikKcmlnaHRwbG90cyA8LSBjb3dwbG90OjpwbG90X2dyaWQoCiAgY293cGxvdDo6cGxvdF9ncmlkKE5VTEwscDJkLE5VTEwsbmNvbD0zLCByZWxfd2lkdGhzID0gYygwLjEsIDAuOCwgMC4xKSksCiAgY293cGxvdDo6cGxvdF9ncmlkKE5VTEwscDJmLE5VTEwsbmNvbD0zLCByZWxfd2lkdGhzID0gYygwLjEsIDAuOCwgMC4xKSksCiAgbmNvbD0yLCBsYWJlbHM9YygnRCcpLCAKICByZWxfd2lkdGhzPWMoMC42LDAuNCkKKQoKYm90cGxvdHMgPC0gY293cGxvdDo6cGxvdF9ncmlkKAogIGxlZnRwbG90cywgcmlnaHRwbG90cywgbmNvbD0yCikKCmYyIDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZCh0aXRsZSwgdG9wcGxvdHMsIGJvdHBsb3RzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoMC4xLDEsMSkpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEZpZ3VyZSAzCiMjIDNBOiBVTUFQIHBsb3QgZm9yIEFzdHJvcwojIyAzQjogQ29tcG9zaXRpb24KIyMgM0M6IE1hcmtlcnMKCklkZW50cyhhc3Ryb3MpIDwtICdzZXVyYXRfY2x1c3RlcnMnCmFzdHJvcyA8LSBTdGFzaElkZW50KGFzdHJvcywgc2F2ZS5uYW1lPSdzZXVyYXRfb3JpZ2luYWxfY2x1c3RlcnMnKQoKYXN0cm9zIDwtIFJlbmFtZUlkZW50cyhhc3Ryb3MsICIwIj0iQXN0cm9jeXRlXG5TdWJUeXBlIDEiKQphc3Ryb3MgPC0gUmVuYW1lSWRlbnRzKGFzdHJvcywgIjEiPSJBc3Ryb2N5dGVcblN1YlR5cGUgMiIpCmFzdHJvcyA8LSBSZW5hbWVJZGVudHMoYXN0cm9zLCAiMiI9IkFzdHJvY3l0ZVxuU3ViVHlwZSAzIikKYXN0cm9zIDwtIFJlbmFtZUlkZW50cyhhc3Ryb3MsICIzIj0iQXN0cm9jeXRlXG5TdWJUeXBlIDQiKQoKYXN0cm9zW1snQXN0cm9Bbm5vdGF0aW9ucyddXSA8LSBJZGVudHMob2JqZWN0PWFzdHJvcykKCnAzYSA8LSBEaW1QbG90KGFzdHJvcywgcHQuc2l6ZT0yLCBsYWJlbD1ULGxhYmVsLnNpemU9My41LCBncm91cC5ieT0nQXN0cm9Bbm5vdGF0aW9ucycsIGNvbHMgPSBjKCJBc3Ryb2N5dGVcblN1YlR5cGUgMSI9IiMwYWQxYzYiLCAiQXN0cm9jeXRlXG5TdWJUeXBlIDIiPSIjMzJmNWVhIiwiQXN0cm9jeXRlXG5TdWJUeXBlIDMiPSIjOWRlY2U4IiwiQXN0cm9jeXRlXG5TdWJUeXBlIDQiPSIjMDc4ODgxIiksIHJlcGVsPVQpICsgTm9MZWdlbmQoKSsgZ2d0aXRsZShOVUxMKQoKCklkZW50cyhhc3Ryb3MpIDwtICdzZXVyYXRfY2x1c3RlcnMnCmFzdHJvcyA8LSBSZW5hbWVJZGVudHMoYXN0cm9zLCAiMCI9IkFTVCAxIikKYXN0cm9zIDwtIFJlbmFtZUlkZW50cyhhc3Ryb3MsICIxIj0iQVNUIDIiKQphc3Ryb3MgPC0gUmVuYW1lSWRlbnRzKGFzdHJvcywgIjIiPSJBU1QgMyIpCmFzdHJvcyA8LSBSZW5hbWVJZGVudHMoYXN0cm9zLCAiMyI9IkFTVCA0IikKYXN0cm9zW1snQXN0cm9Bbm5vdGF0aW9uczInXV0gPC0gSWRlbnRzKG9iamVjdD1hc3Ryb3MpCgoKSWRlbnRzKGFzdHJvcykgPC0gJ0FzdHJvQW5ub3RhdGlvbnMyJwpjb3VudHMgPC0gdGFibGUoSWRlbnRzKGFzdHJvcyksIGFzdHJvcyRzYW1wKQpjb3VudHMgPC0gcHJvcC50YWJsZShjb3VudHMsIG1hcmdpbj0yKSoxMDAKI09yZ2FuaXplIHRoZSBkYXRhZnJhbWUgZm9yIGVhc3kgcGxvdHRpbmcKZGYgPC0gYXMuZGF0YS5mcmFtZS5tYXRyaXgoY291bnRzKSAKZGYkY2x1c3RlciA8LSByb3duYW1lcyhkZikKZGYgPC0gZGYgJT4lIGdhdGhlciguLCB0cmVhdG1lbnQsIHByb3BzLCBDb250cm9sOkNvbnRyb2wsIGZhY3Rvcl9rZXk9VCkgCmdyb3VwLmNvbG9yczIgPC0gYygiQVNUIDEiPSIjMGFkMWM2IiwgIkFTVCAyIj0iIzMyZjVlYSIsIkFTVCAzIj0iIzlkZWNlOCIsIkFTVCA0Ij0iIzA3ODg4MSIpCnAzYiA8LSBnZ3Bsb3QoZGYsIGFlcyh4PXRyZWF0bWVudCwgeT1wcm9wcywgZmlsbD1jbHVzdGVyLCBsYWJlbD1jbHVzdGVyKSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIGNvbG9yPSdibGFjaycpICsgdGhlbWVfbWluaW1hbCgpICsgbGFicyh0aXRsZT0nJyx4PScnLHk9J1BlcmNlbnRhZ2Ugb2YgY2VsbHMnLCBmaWxsPSdDbHVzdGVyOicpICsgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNSksIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzE1LCBzaXplPTApKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1ncm91cC5jb2xvcnMyLGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKSArIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCnBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSkrIE5vTGVnZW5kKCkrCiAgZ2VvbV90ZXh0KHNpemUgPSAyLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkKCmFzdHJvX2dlbmVzIDwtIGMoJ1NveDYnLCdOY2thcDUnLCdLY25pcDQnLCdOcmczJywnRm4xJywnVGVrJywnU3lwJywnQ2Vsc3IyJykKb3JkZXJlZF9sZXZlbCA8LSBjKCJBc3Ryb2N5dGVcblN1YlR5cGUgMSIsIkFzdHJvY3l0ZVxuU3ViVHlwZSAyIiwiQXN0cm9jeXRlXG5TdWJUeXBlIDMiLCJBc3Ryb2N5dGVcblN1YlR5cGUgNCIpCmFzdHJvc0BtZXRhLmRhdGEkQXN0cm9Bbm5vdGF0aW9ucyA8LSBmYWN0b3IoeD1hc3Ryb3NAbWV0YS5kYXRhJEFzdHJvQW5ub3RhdGlvbnMsbGV2ZWxzPW9yZGVyZWRfbGV2ZWwpCnAzYyA8LSBWbG5QbG90KGFzdHJvcywgZmVhdHVyZXM9Yyhhc3Ryb19nZW5lcyksIHN0YWNrPVRSVUUsIHNvcnQ9RkFMU0UsIGZsaXA9VFJVRSxncm91cC5ieT0nQXN0cm9Bbm5vdGF0aW9ucycsY29scz1jKCJBc3Ryb2N5dGVcblN1YlR5cGUgMSI9IiMwYWQxYzYiLCAiQXN0cm9jeXRlXG5TdWJUeXBlIDIiPSIjMzJmNWVhIiwiQXN0cm9jeXRlXG5TdWJUeXBlIDMiPSIjOWRlY2U4IiwiQXN0cm9jeXRlXG5TdWJUeXBlIDQiPSIjMDc4ODgxIiksIGZpbGwuYnk9J2lkZW50JykgKyB0aGVtZShsZWdlbmQucG9zaXRpb249J25vbmUnKSArIGxhYnModGl0bGU9JycseD0nJykgKyB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE1KSxheGlzLnRleHQueCA9ZWxlbWVudF90ZXh0KHNpemU9OSksYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKStzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1mdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkKICAKCnRpdGxlIDwtIGdnZHJhdygpICsgCiAgZHJhd19sYWJlbCgKICAgICJGaWd1cmUgMyIsCiAgICBmb250ZmFjZSA9ICdib2xkJywKICAgIHggPSAwLAogICAgaGp1c3QgPSAwLAogICAgc2l6ZT0yMAogICkgKwogIHRoZW1lKAogICAgIyBhZGQgbWFyZ2luIG9uIHRoZSBsZWZ0IG9mIHRoZSBkcmF3aW5nIGNhbnZhcywKICAgICMgc28gdGl0bGUgaXMgYWxpZ25lZCB3aXRoIGxlZnQgZWRnZSBvZiBmaXJzdCBwbG90CiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCAwLCAwLCA3KQogICkKCgpib3RwbG90cyA8LSBjb3dwbG90OjpwbG90X2dyaWQoCiAgY293cGxvdDo6cGxvdF9ncmlkKE5VTEwscDNjLE5VTEwsbmNvbD0zLCByZWxfd2lkdGhzID0gYygwLjEsIDAuOCwgMC4xKSksCiAgY293cGxvdDo6cGxvdF9ncmlkKE5VTEwscDNiLE5VTEwsbmNvbD0zLCByZWxfd2lkdGhzID0gYygwLjEsIDAuOCwgMC4xKSksCiAgbmNvbD0yLCBsYWJlbHM9YygnQicpLCAKICByZWxfd2lkdGhzPWMoMC42LDAuNCkKKQoKcGxvdHMgPC0gY293cGxvdDo6cGxvdF9ncmlkKGNvd3Bsb3Q6OnBsb3RfZ3JpZChOVUxMLHAzYSxOVUxMLG5jb2w9MywgcmVsX3dpZHRocyA9IGMoMC4xLCAwLjgsIDAuMSksbGFiZWxzPWMoIkEiKSksIGJvdHBsb3RzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoMSwxKSkKZjMgPC0gY293cGxvdDo6cGxvdF9ncmlkKHRpdGxlLCBwbG90cywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDAuMSwxKSkKCmYzcGxvdCA8LSBjb3dwbG90OjpwbG90X2dyaWQocDNhLCBwM2MscDNiLCBsYWJlbHM9YygnQScsJ0InKSwgbmNvbD0zLCByZWxfd2lkdGhzPWMoMC44LCAwLjYsIDAuNCkpIAoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRmlndXJlIDQKIyMgNEE6IEFzdHJvcyBVcHNldCAobWF5YmUgdmVubj8pCiMjIDRCOiBCdWJibGUgcGxvdAoKY2x1c3QwIDwtIHJlYWQuY3N2KCcuL09SQV9hc3Ryb3MvR08vQlAvY2x1c3Rlcl8wX0dPX0JQX09SQS5jc3YnLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gMSxzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmNsdXN0MCRDbHVzdGVyIDwtIGMoJ0FzdHJvY3l0ZSBTdWJUeXBlIDEnKQoKY2x1c3QxIDwtIHJlYWQuY3N2KCcuL09SQV9hc3Ryb3MvR08vQlAvY2x1c3Rlcl8xX0dPX0JQX09SQS5jc3YnLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gMSxzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmNsdXN0MSRDbHVzdGVyIDwtIGMoJ0FzdHJvY3l0ZSBTdWJUeXBlIDInKQoKY2x1c3QyIDwtIHJlYWQuY3N2KCcuL09SQV9hc3Ryb3MvR08vQlAvY2x1c3Rlcl8yX0dPX0JQX09SQS5jc3YnLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gMSxzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmNsdXN0MiRDbHVzdGVyIDwtIGMoJ0FzdHJvY3l0ZSBTdWJUeXBlIDMnKQoKY2x1c3QzIDwtIHJlYWQuY3N2KCcuL09SQV9hc3Ryb3MvR08vQlAvY2x1c3Rlcl8zX0dPX0JQX09SQS5jc3YnLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gMSkKY2x1c3QzJENsdXN0ZXIgPC0gYygnQXN0cm9jeXRlIFN1YlR5cGUgNCcpCgptZXJnZWQgPC0gYmluZF9yb3dzKGNsdXN0MCwgY2x1c3QxLCBjbHVzdDIsIGNsdXN0MykKZ29saXN0IDwtIGMoJ3N5bmFwc2UgYXNzZW1ibHknLCdyZWd1bGF0aW9uIG9mIHN5bmFwc2Ugb3JnYW5pemF0aW9uJywnY2VsbCBqdW5jdGlvbiBhc3NlbWJseScsJ3JlZ3VsYXRpb24gb2Ygc3luYXBzZSBzdHJ1Y3R1cmUgb3IgYWN0aXZpdHknLCdzaWduYWwgcmVsZWFzZSBmcm9tIHN5bmFwc2UnLCduZXVyb3RyYW5zbWl0dGVyIHNlY3JldGlvbicsJ3Zlc2ljbGUtbWVkaWF0ZWQgdHJhbnNwb3J0IGluIHN5bmFwc2UnLCdzeW5hcHRpYyB2ZXNpY2xlIGN5Y2xlJywncmVndWxhdGlvbiBvZiBuZXVyb3RyYW5zbWl0dGVyIHRyYW5zcG9ydCcsJ3N5bmFwdGljIHRyYW5zbWlzc2lvbiwgZ2x1dGFtYXRlcmdpYycsJ3JlZ3VsYXRpb24gb2Ygc3luYXB0aWMgcGxhc3RpY2l0eScsJ2xlYXJuaW5nIG9yIG1lbW9yeScsJ2NvZ25pdGlvbicsJ2F4b25vZ2VuZXNpcycsJ3JlZ3VsYXRpb24gb2YgbXllbGluYXRpb24nLCdvbGlnb2RlbmRyb2N5dGUgZGlmZmVyZW50aWF0aW9uJywndmFzY3Vsb2dlbmVzaXMnLCdyZWd1bGF0aW9uIG9mIGFuZ2lvZ2VuZXNpcycsJ3JlZ3VsYXRpb24gb2Ygdm9sdGFnZS1nYXRlZCBjYWxjaXVtIGNoYW5uZWwgYWN0aXZpdHknLCdzeW5hcHRpYyBtZW1icmFuZSBhZGhlc2lvbicsJ3ByZXN5bmFwdGljIGVuZG9jeXRvc2lzJywnc3luYXB0aWMgdmVzaWNsZSByZWN5Y2xpbmcnKQoKbWVyZ2VkIDwtIG1lcmdlZFttZXJnZWQkRGVzY3JpcHRpb24gJWluJSBnb2xpc3QsXQoKcDRiIDwtIGdncGxvdChtZXJnZWQsIGFlcyh4PUNsdXN0ZXIsIHk9RGVzY3JpcHRpb24sIHNpemU9Q291bnQsY29sb3I9cC5hZGp1c3QsIGdyb3VwPUNsdXN0ZXIpKSArIGdlb21fcG9pbnQoYWxwaGE9MC44KSArIHRoZW1lX2NsYXNzaWMoKStzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAicmVkMiIsICBoaWdoID0gIm1lZGl1bWJsdWUiLCBzcGFjZSA9ICJMYWIiKStzY2FsZV9zaXplKHJhbmdlID0gYygyLCA4KSkgKyBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscz1mdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDI1KSkrIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTApKSsgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArIHlsYWIoJ0dPIFRlcm1zJykKCmxpYnJhcnkoVXBTZXRSKQp1cHNldHMgPC0gbGlzdCgpCnVwc2V0c1tbJ0FzdHJvY3l0ZVxuU3ViVHlwZSAxJ11dIDwtIGNsdXN0MCREZXNjcmlwdGlvbgp1cHNldHNbWydBc3Ryb2N5dGVcblN1YlR5cGUgMiddXSA8LSBjbHVzdDEkRGVzY3JpcHRpb24KdXBzZXRzW1snQXN0cm9jeXRlXG5TdWJUeXBlIDMnXV0gPC0gY2x1c3QyJERlc2NyaXB0aW9uCnVwc2V0c1tbJ0FzdHJvY3l0ZVxuU3ViVHlwZSA0J11dIDwtIGNsdXN0MyREZXNjcmlwdGlvbgoKcDRhIDwtIHVwc2V0KGZyb21MaXN0KHVwc2V0cyksIG5zZXRzPTE1LCBvcmRlci5ieT0nZnJlcScsdGV4dC5zY2FsZT1jKDIsMiwyLDIsMiwyKSwKICAgICBwb2ludC5zaXplPTYsCiAgICAgbGluZS5zaXplPTQsCiAgICAgbWFpbmJhci55LmxhYmVsPSdTaWduaWZpY2FudCBHTyBwYXRod2F5IGludGVyc2VjdGlvbnMnLCBzZXRzLngubGFiZWw9J1NpZ25pZmljYW50IEdPIHBhdGh3YXlzJykKCmxpYnJhcnkoVmVubkRpYWdyYW0pCnZlbm4gPC0gdmVubi5kaWFncmFtKAogIHVwc2V0cywKICBmaWxsPWMoIiMwYWQxYzYiLCIjMzJmNWVhIiwiIzlkZWNlOCIsIiMwNzg4ODEiKSwKICBsd2Q9MSwKICBsdHk9MSwKICBjYXQuY2V4PTEsCiAgY2V4PTEuNSwKICBhbHBoYT0xLAogIGZpbGVuYW1lPU5VTEwKKQoKdGl0bGUgPC0gZ2dkcmF3KCkgKyAKICBkcmF3X2xhYmVsKAogICAgIkZpZ3VyZSA0IiwKICAgIGZvbnRmYWNlID0gJ2JvbGQnLAogICAgeCA9IDAsCiAgICBoanVzdCA9IDAsCiAgICBzaXplPTIwCiAgKSArCiAgdGhlbWUoCiAgICAjIGFkZCBtYXJnaW4gb24gdGhlIGxlZnQgb2YgdGhlIGRyYXdpbmcgY2FudmFzLAogICAgIyBzbyB0aXRsZSBpcyBhbGlnbmVkIHdpdGggbGVmdCBlZGdlIG9mIGZpcnN0IHBsb3QKICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDcpCiAgKQoKcGxvdHMgPC0gY293cGxvdDo6cGxvdF9ncmlkKHZlbm4scDRiLCBuY29sPTIsIHJlbF93aWR0aHM9YygwLjUsMC41KSwgbGFiZWxzID0gYygiQSIsIkIiKSkKZjQgPC0gY293cGxvdDo6cGxvdF9ncmlkKHRpdGxlLCBwbG90cywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDAuMSwxKSkKCnBkZignLi9maWd1cmUxLnBkZicsaGVpZ2h0PTgsIHdpZHRoPTEyKQpwcmludChmMSkKZGV2Lm9mZigpCgpwZGYoJy4vZmlndXJlMi5wZGYnLGhlaWdodD04LCB3aWR0aD0xMikKcHJpbnQoZjIpCmRldi5vZmYoKQoKcGRmKCcuL2ZpZ3VyZTMucGRmJyxoZWlnaHQ9OCwgd2lkdGg9NikKcHJpbnQoZjMpCmRldi5vZmYoKQoKcGRmKCcuL2ZpZ3VyZTNfaG9yaXpvbnRhbC5wZGYnLGhlaWdodD00LCB3aWR0aD0yMCkKcHJpbnQoZjNwbG90KQpkZXYub2ZmKCkKCnBkZignLi9maWd1cmU0LnBkZicsaGVpZ2h0PTgsIHdpZHRoPTEyKQpwcmludChmNCkKZGV2Lm9mZigpCgpmMQpmMgpmMwpmNApgYGAKCmBgYHtyIFNlc3Npb25JbmZvfQpzZXNzaW9uSW5mbygpCmBgYA==